1 # Copyright 2018-2019 The glTF-Blender-IO authors.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
16 'name': 'glTF 2.0 format',
17 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
19 'blender': (2, 91, 0),
20 'location': 'File > Import-Export',
21 'description': 'Import-Export as glTF 2.0',
23 'doc_url': "{BLENDER_MANUAL_URL}/addons/import_export/scene_gltf2.html",
24 'tracker_url': "https://github.com/KhronosGroup/glTF-Blender-IO/issues/",
25 'support': 'OFFICIAL',
26 'category': 'Import-Export',
29 def get_version_string():
30 return str(bl_info
['version'][0]) + '.' + str(bl_info
['version'][1]) + '.' + str(bl_info
['version'][2])
33 # Script reloading (if the user calls 'Reload Scripts' from Blender)
36 def reload_package(module_dict_main
):
38 from pathlib
import Path
40 def reload_package_recursive(current_dir
, module_dict
):
41 for path
in current_dir
.iterdir():
42 if "__init__" in str(path
) or path
.stem
not in module_dict
:
45 if path
.is_file() and path
.suffix
== ".py":
46 importlib
.reload(module_dict
[path
.stem
])
48 reload_package_recursive(path
, module_dict
[path
.stem
].__dict
__)
50 reload_package_recursive(Path(__file__
).parent
, module_dict_main
)
54 reload_package(locals())
57 from bpy
.props
import (StringProperty
,
62 from bpy
.types
import Operator
63 from bpy_extras
.io_utils
import ImportHelper
, ExportHelper
67 # Functions / Classes.
70 extension_panel_unregister_functors
= []
73 def on_export_format_changed(self
, context
):
74 # Update the file extension when the format (.glb/.gltf) changes
75 sfile
= context
.space_data
76 operator
= sfile
.active_operator
77 if operator
.bl_idname
!= "EXPORT_SCENE_OT_gltf":
79 if operator
.check(context
):
80 # Weird hack to force the filepicker to notice filename changed
81 from os
.path
import basename
82 filepath
= operator
.filepath
83 bpy
.ops
.file.filenum(increment
=-1)
84 if basename(operator
.filepath
) != basename(filepath
):
85 bpy
.ops
.file.filenum(increment
=1)
88 class ExportGLTF2_Base
:
89 # TODO: refactor to avoid boilerplate
92 from io_scene_gltf2
.io
.exp
import gltf2_io_draco_compression_extension
93 self
.is_draco_available
= gltf2_io_draco_compression_extension
.dll_exists()
95 bl_options
= {'PRESET'}
97 export_format
: EnumProperty(
99 items
=(('GLB', 'glTF Binary (.glb)',
100 'Exports a single file, with all data packed in binary form. '
101 'Most efficient and portable, but more difficult to edit later'),
102 ('GLTF_EMBEDDED', 'glTF Embedded (.gltf)',
103 'Exports a single file, with all data packed in JSON. '
104 'Less efficient than binary, but easier to edit later'),
105 ('GLTF_SEPARATE', 'glTF Separate (.gltf + .bin + textures)',
106 'Exports multiple files, with separate JSON, binary and texture data. '
107 'Easiest to edit later')),
109 'Output format and embedding options. Binary is most efficient, '
110 'but JSON (embedded or separate) may be easier to edit later'
113 update
=on_export_format_changed
,
116 ui_tab
: EnumProperty(
117 items
=(('GENERAL', "General", "General settings"),
118 ('MESHES', "Meshes", "Mesh settings"),
119 ('OBJECTS', "Objects", "Object settings"),
120 ('ANIMATION', "Animation", "Animation settings")),
122 description
="Export setting categories",
125 export_copyright
: StringProperty(
127 description
='Legal rights and conditions for the model',
131 export_image_format
: EnumProperty(
133 items
=(('AUTO', 'Automatic',
134 'Save PNGs as PNGs and JPEGs as JPEGs. '
135 'If neither one, use PNG'),
136 ('JPEG', 'JPEG Format (.jpg)',
137 'Save images as JPEGs. (Images that need alpha are saved as PNGs though.) '
138 'Be aware of a possible loss in quality'),
141 'Output format for images. PNG is lossless and generally preferred, but JPEG might be preferable for web '
142 'applications due to the smaller file size'
147 export_texture_dir
: StringProperty(
149 description
='Folder to place texture files in. Relative to the .gltf file',
153 export_texcoords
: BoolProperty(
155 description
='Export UVs (texture coordinates) with meshes',
159 export_normals
: BoolProperty(
161 description
='Export vertex normals with meshes',
165 export_draco_mesh_compression_enable
: BoolProperty(
166 name
='Draco mesh compression',
167 description
='Compress mesh using Draco',
171 export_draco_mesh_compression_level
: IntProperty(
172 name
='Compression level',
173 description
='Compression level (0 = most speed, 6 = most compression, higher values currently not supported)',
179 export_draco_position_quantization
: IntProperty(
180 name
='Position quantization bits',
181 description
='Quantization bits for position values (0 = no quantization)',
187 export_draco_normal_quantization
: IntProperty(
188 name
='Normal quantization bits',
189 description
='Quantization bits for normal values (0 = no quantization)',
195 export_draco_texcoord_quantization
: IntProperty(
196 name
='Texcoord quantization bits',
197 description
='Quantization bits for texture coordinate values (0 = no quantization)',
203 export_draco_generic_quantization
: IntProperty(
204 name
='Generic quantization bits',
205 description
='Quantization bits for generic coordinate values like weights or joints (0 = no quantization)',
211 export_tangents
: BoolProperty(
213 description
='Export vertex tangents with meshes',
217 export_materials
: EnumProperty(
219 items
=(('EXPORT', 'Export',
220 'Export all materials used by included objects'),
221 ('PLACEHOLDER', 'Placeholder',
222 'Do not export materials, but write multiple primitive groups per mesh, keeping material slot information'),
223 ('NONE', 'No export',
224 'Do not export materials, and combine mesh primitive groups, losing material slot information')),
225 description
='Export materials ',
229 export_colors
: BoolProperty(
230 name
='Vertex Colors',
231 description
='Export vertex colors with meshes',
235 export_cameras
: BoolProperty(
237 description
='Export cameras',
241 # keep it for compatibility (for now)
242 export_selected
: BoolProperty(
243 name
='Selected Objects',
244 description
='Export selected objects only',
248 use_selection
: BoolProperty(
249 name
='Selected Objects',
250 description
='Export selected objects only',
254 export_extras
: BoolProperty(
255 name
='Custom Properties',
256 description
='Export custom properties as glTF extras',
260 export_yup
: BoolProperty(
262 description
='Export using glTF convention, +Y up',
266 export_apply
: BoolProperty(
267 name
='Apply Modifiers',
268 description
='Apply modifiers (excluding Armatures) to mesh objects -'
269 'WARNING: prevents exporting shape keys',
273 export_animations
: BoolProperty(
275 description
='Exports active actions and NLA tracks as glTF animations',
279 export_frame_range
: BoolProperty(
280 name
='Limit to Playback Range',
281 description
='Clips animations to selected playback range',
285 export_frame_step
: IntProperty(
286 name
='Sampling Rate',
287 description
='How often to evaluate animated values (in frames)',
293 export_force_sampling
: BoolProperty(
294 name
='Always Sample Animations',
295 description
='Apply sampling to all animations',
299 export_nla_strips
: BoolProperty(
300 name
='Group by NLA Track',
302 "When on, multiple actions become part of the same glTF animation if "
303 "they're pushed onto NLA tracks with the same name. "
304 "When off, all the currently assigned actions become one glTF animation"
309 export_def_bones
: BoolProperty(
310 name
='Export Deformation Bones Only',
311 description
='Export Deformation bones only (and needed bones for hierarchy)',
315 export_current_frame
: BoolProperty(
316 name
='Use Current Frame',
317 description
='Export the scene in the current animation frame',
321 export_skins
: BoolProperty(
323 description
='Export skinning (armature) data',
327 export_all_influences
: BoolProperty(
328 name
='Include All Bone Influences',
329 description
='Allow >4 joint vertex influences. Models may appear incorrectly in many viewers',
333 export_morph
: BoolProperty(
335 description
='Export shape keys (morph targets)',
339 export_morph_normal
: BoolProperty(
340 name
='Shape Key Normals',
341 description
='Export vertex normals with shape keys (morph targets)',
345 export_morph_tangent
: BoolProperty(
346 name
='Shape Key Tangents',
347 description
='Export vertex tangents with shape keys (morph targets)',
351 export_lights
: BoolProperty(
352 name
='Punctual Lights',
353 description
='Export directional, point, and spot lights. '
354 'Uses "KHR_lights_punctual" glTF extension',
358 export_displacement
: BoolProperty(
359 name
='Displacement Textures (EXPERIMENTAL)',
360 description
='EXPERIMENTAL: Export displacement textures. '
361 'Uses incomplete "KHR_materials_displacement" glTF extension',
365 will_save_settings
: BoolProperty(
366 name
='Remember Export Settings',
367 description
='Store glTF export settings in the Blender project',
370 # Custom scene property for saving settings
371 scene_key
= "glTF2ExportSettings"
375 def check(self
, _context
):
376 # Ensure file extension matches format
378 filename
= os
.path
.basename(self
.filepath
)
380 filepath
= self
.filepath
381 desired_ext
= '.glb' if self
.export_format
== 'GLB' else '.gltf'
383 stem
, ext
= os
.path
.splitext(filename
)
384 if stem
.startswith('.') and not ext
:
387 ext_lower
= ext
.lower()
388 if ext_lower
not in ['.glb', '.gltf']:
389 filepath
= filepath
+ desired_ext
390 elif ext_lower
!= desired_ext
:
391 filepath
= filepath
[:-len(ext
)] # strip off ext
392 filepath
+= desired_ext
394 if filepath
!= self
.filepath
:
395 self
.filepath
= filepath
400 def invoke(self
, context
, event
):
401 settings
= context
.scene
.get(self
.scene_key
)
402 self
.will_save_settings
= False
405 for (k
, v
) in settings
.items():
406 if k
== "export_selected": # Back compatibility for export_selected --> use_selection
407 setattr(self
, "use_selection", v
)
409 settings
["use_selection"] = v
410 print("export_selected is now renamed use_selection, and will be deleted in a few release")
413 self
.will_save_settings
= True
415 except (AttributeError, TypeError):
416 self
.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings")
417 del context
.scene
[self
.scene_key
]
420 preferences
= bpy
.context
.preferences
421 for addon_name
in preferences
.addons
.keys():
423 if hasattr(sys
.modules
[addon_name
], 'glTF2ExportUserExtension') or hasattr(sys
.modules
[addon_name
], 'glTF2ExportUserExtensions'):
424 extension_panel_unregister_functors
.append(sys
.modules
[addon_name
].register_panel())
428 self
.has_active_extenions
= len(extension_panel_unregister_functors
) > 0
429 return ExportHelper
.invoke(self
, context
, event
)
431 def save_settings(self
, context
):
432 # find all export_ props
433 all_props
= self
.properties
434 export_props
= {x
: getattr(self
, x
) for x
in dir(all_props
)
435 if (x
.startswith("export_") or x
== "use_selection") and all_props
.get(x
) is not None}
437 context
.scene
[self
.scene_key
] = export_props
439 def execute(self
, context
):
442 from .blender
.exp
import gltf2_blender_export
444 if self
.will_save_settings
:
445 self
.save_settings(context
)
447 self
.check(context
) # ensure filepath has the right extension
449 # All custom export settings are stored in this container.
452 export_settings
['timestamp'] = datetime
.datetime
.now()
454 export_settings
['gltf_filepath'] = self
.filepath
455 export_settings
['gltf_filedirectory'] = os
.path
.dirname(export_settings
['gltf_filepath']) + '/'
456 export_settings
['gltf_texturedirectory'] = os
.path
.join(
457 export_settings
['gltf_filedirectory'],
458 self
.export_texture_dir
,
461 export_settings
['gltf_format'] = self
.export_format
462 export_settings
['gltf_image_format'] = self
.export_image_format
463 export_settings
['gltf_copyright'] = self
.export_copyright
464 export_settings
['gltf_texcoords'] = self
.export_texcoords
465 export_settings
['gltf_normals'] = self
.export_normals
466 export_settings
['gltf_tangents'] = self
.export_tangents
and self
.export_normals
468 if self
.is_draco_available
:
469 export_settings
['gltf_draco_mesh_compression'] = self
.export_draco_mesh_compression_enable
470 export_settings
['gltf_draco_mesh_compression_level'] = self
.export_draco_mesh_compression_level
471 export_settings
['gltf_draco_position_quantization'] = self
.export_draco_position_quantization
472 export_settings
['gltf_draco_normal_quantization'] = self
.export_draco_normal_quantization
473 export_settings
['gltf_draco_texcoord_quantization'] = self
.export_draco_texcoord_quantization
474 export_settings
['gltf_draco_generic_quantization'] = self
.export_draco_generic_quantization
476 export_settings
['gltf_draco_mesh_compression'] = False
478 export_settings
['gltf_materials'] = self
.export_materials
479 export_settings
['gltf_colors'] = self
.export_colors
480 export_settings
['gltf_cameras'] = self
.export_cameras
482 # compatibility after renaming export_selected to use_selection
483 if self
.export_selected
is True:
484 self
.report({"WARNING"}, "export_selected is now renamed use_selection, and will be deleted in a few release")
485 export_settings
['gltf_selected'] = self
.export_selected
487 export_settings
['gltf_selected'] = self
.use_selection
489 # export_settings['gltf_selected'] = self.use_selection This can be uncomment when removing compatibility of export_selected
490 export_settings
['gltf_layers'] = True # self.export_layers
491 export_settings
['gltf_extras'] = self
.export_extras
492 export_settings
['gltf_yup'] = self
.export_yup
493 export_settings
['gltf_apply'] = self
.export_apply
494 export_settings
['gltf_current_frame'] = self
.export_current_frame
495 export_settings
['gltf_animations'] = self
.export_animations
496 if self
.export_animations
:
497 export_settings
['gltf_frame_range'] = self
.export_frame_range
498 export_settings
['gltf_force_sampling'] = self
.export_force_sampling
499 if self
.export_force_sampling
:
500 export_settings
['gltf_def_bones'] = self
.export_def_bones
502 export_settings
['gltf_def_bones'] = False
503 export_settings
['gltf_nla_strips'] = self
.export_nla_strips
505 export_settings
['gltf_frame_range'] = False
506 export_settings
['gltf_move_keyframes'] = False
507 export_settings
['gltf_force_sampling'] = False
508 export_settings
['gltf_def_bones'] = False
509 export_settings
['gltf_skins'] = self
.export_skins
510 if self
.export_skins
:
511 export_settings
['gltf_all_vertex_influences'] = self
.export_all_influences
513 export_settings
['gltf_all_vertex_influences'] = False
514 export_settings
['gltf_frame_step'] = self
.export_frame_step
515 export_settings
['gltf_morph'] = self
.export_morph
516 if self
.export_morph
:
517 export_settings
['gltf_morph_normal'] = self
.export_morph_normal
519 export_settings
['gltf_morph_normal'] = False
520 if self
.export_morph
and self
.export_morph_normal
:
521 export_settings
['gltf_morph_tangent'] = self
.export_morph_tangent
523 export_settings
['gltf_morph_tangent'] = False
525 export_settings
['gltf_lights'] = self
.export_lights
526 export_settings
['gltf_displacement'] = self
.export_displacement
528 export_settings
['gltf_binary'] = bytearray()
529 export_settings
['gltf_binaryfilename'] = (
530 os
.path
.splitext(os
.path
.basename(self
.filepath
))[0] + '.bin'
534 pre_export_callbacks
= []
535 post_export_callbacks
= []
538 preferences
= bpy
.context
.preferences
539 for addon_name
in preferences
.addons
.keys():
541 module
= sys
.modules
[addon_name
]
544 if hasattr(module
, 'glTF2ExportUserExtension'):
545 extension_ctor
= module
.glTF2ExportUserExtension
546 user_extensions
.append(extension_ctor())
547 if hasattr(module
, 'glTF2ExportUserExtensions'):
548 extension_ctors
= module
.glTF2ExportUserExtensions
549 for extension_ctor
in extension_ctors
:
550 user_extensions
.append(extension_ctor())
551 if hasattr(module
, 'glTF2_pre_export_callback'):
552 pre_export_callbacks
.append(module
.glTF2_pre_export_callback
)
553 if hasattr(module
, 'glTF2_post_export_callback'):
554 post_export_callbacks
.append(module
.glTF2_post_export_callback
)
555 export_settings
['gltf_user_extensions'] = user_extensions
556 export_settings
['pre_export_callbacks'] = pre_export_callbacks
557 export_settings
['post_export_callbacks'] = post_export_callbacks
559 return gltf2_blender_export
.save(context
, export_settings
)
561 def draw(self
, context
):
562 pass # Is needed to get panels available
565 class GLTF_PT_export_main(bpy
.types
.Panel
):
566 bl_space_type
= 'FILE_BROWSER'
567 bl_region_type
= 'TOOL_PROPS'
569 bl_parent_id
= "FILE_PT_operator"
570 bl_options
= {'HIDE_HEADER'}
573 def poll(cls
, context
):
574 sfile
= context
.space_data
575 operator
= sfile
.active_operator
577 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
579 def draw(self
, context
):
581 layout
.use_property_split
= True
582 layout
.use_property_decorate
= False # No animation.
584 sfile
= context
.space_data
585 operator
= sfile
.active_operator
587 layout
.prop(operator
, 'export_format')
588 if operator
.export_format
== 'GLTF_SEPARATE':
589 layout
.prop(operator
, 'export_texture_dir', icon
='FILE_FOLDER')
590 layout
.prop(operator
, 'export_copyright')
591 layout
.prop(operator
, 'will_save_settings')
594 class GLTF_PT_export_include(bpy
.types
.Panel
):
595 bl_space_type
= 'FILE_BROWSER'
596 bl_region_type
= 'TOOL_PROPS'
598 bl_parent_id
= "FILE_PT_operator"
599 bl_options
= {'DEFAULT_CLOSED'}
602 def poll(cls
, context
):
603 sfile
= context
.space_data
604 operator
= sfile
.active_operator
606 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
608 def draw(self
, context
):
610 layout
.use_property_split
= True
611 layout
.use_property_decorate
= False # No animation.
613 sfile
= context
.space_data
614 operator
= sfile
.active_operator
616 col
= layout
.column(heading
= "Limit to", align
= True)
617 col
.prop(operator
, 'use_selection')
619 col
= layout
.column(heading
= "Data", align
= True)
620 col
.prop(operator
, 'export_extras')
621 col
.prop(operator
, 'export_cameras')
622 col
.prop(operator
, 'export_lights')
625 class GLTF_PT_export_transform(bpy
.types
.Panel
):
626 bl_space_type
= 'FILE_BROWSER'
627 bl_region_type
= 'TOOL_PROPS'
628 bl_label
= "Transform"
629 bl_parent_id
= "FILE_PT_operator"
630 bl_options
= {'DEFAULT_CLOSED'}
633 def poll(cls
, context
):
634 sfile
= context
.space_data
635 operator
= sfile
.active_operator
637 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
639 def draw(self
, context
):
641 layout
.use_property_split
= True
642 layout
.use_property_decorate
= False # No animation.
644 sfile
= context
.space_data
645 operator
= sfile
.active_operator
647 layout
.prop(operator
, 'export_yup')
650 class GLTF_PT_export_geometry(bpy
.types
.Panel
):
651 bl_space_type
= 'FILE_BROWSER'
652 bl_region_type
= 'TOOL_PROPS'
653 bl_label
= "Geometry"
654 bl_parent_id
= "FILE_PT_operator"
655 bl_options
= {'DEFAULT_CLOSED'}
658 def poll(cls
, context
):
659 sfile
= context
.space_data
660 operator
= sfile
.active_operator
662 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
664 def draw(self
, context
):
666 layout
.use_property_split
= True
667 layout
.use_property_decorate
= False # No animation.
669 sfile
= context
.space_data
670 operator
= sfile
.active_operator
672 layout
.prop(operator
, 'export_apply')
673 layout
.prop(operator
, 'export_texcoords')
674 layout
.prop(operator
, 'export_normals')
675 col
= layout
.column()
676 col
.active
= operator
.export_normals
677 col
.prop(operator
, 'export_tangents')
678 layout
.prop(operator
, 'export_colors')
679 layout
.prop(operator
, 'export_materials')
680 col
= layout
.column()
681 col
.active
= operator
.export_materials
== "EXPORT"
682 col
.prop(operator
, 'export_image_format')
685 class GLTF_PT_export_geometry_compression(bpy
.types
.Panel
):
686 bl_space_type
= 'FILE_BROWSER'
687 bl_region_type
= 'TOOL_PROPS'
688 bl_label
= "Compression"
689 bl_parent_id
= "GLTF_PT_export_geometry"
690 bl_options
= {'DEFAULT_CLOSED'}
693 from io_scene_gltf2
.io
.exp
import gltf2_io_draco_compression_extension
694 self
.is_draco_available
= gltf2_io_draco_compression_extension
.dll_exists(quiet
=True)
697 def poll(cls
, context
):
698 sfile
= context
.space_data
699 operator
= sfile
.active_operator
700 if operator
.is_draco_available
:
701 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
703 def draw_header(self
, context
):
704 sfile
= context
.space_data
705 operator
= sfile
.active_operator
706 self
.layout
.prop(operator
, "export_draco_mesh_compression_enable", text
="")
708 def draw(self
, context
):
710 layout
.use_property_split
= True
711 layout
.use_property_decorate
= False # No animation.
713 sfile
= context
.space_data
714 operator
= sfile
.active_operator
716 layout
.active
= operator
.export_draco_mesh_compression_enable
717 layout
.prop(operator
, 'export_draco_mesh_compression_level')
719 col
= layout
.column(align
=True)
720 col
.prop(operator
, 'export_draco_position_quantization', text
="Quantize Position")
721 col
.prop(operator
, 'export_draco_normal_quantization', text
="Normal")
722 col
.prop(operator
, 'export_draco_texcoord_quantization', text
="Tex Coords")
723 col
.prop(operator
, 'export_draco_generic_quantization', text
="Generic")
726 class GLTF_PT_export_animation(bpy
.types
.Panel
):
727 bl_space_type
= 'FILE_BROWSER'
728 bl_region_type
= 'TOOL_PROPS'
729 bl_label
= "Animation"
730 bl_parent_id
= "FILE_PT_operator"
731 bl_options
= {'DEFAULT_CLOSED'}
734 def poll(cls
, context
):
735 sfile
= context
.space_data
736 operator
= sfile
.active_operator
738 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
740 def draw(self
, context
):
742 layout
.use_property_split
= True
743 layout
.use_property_decorate
= False # No animation.
745 sfile
= context
.space_data
746 operator
= sfile
.active_operator
748 layout
.prop(operator
, 'export_current_frame')
751 class GLTF_PT_export_animation_export(bpy
.types
.Panel
):
752 bl_space_type
= 'FILE_BROWSER'
753 bl_region_type
= 'TOOL_PROPS'
754 bl_label
= "Animation"
755 bl_parent_id
= "GLTF_PT_export_animation"
756 bl_options
= {'DEFAULT_CLOSED'}
759 def poll(cls
, context
):
760 sfile
= context
.space_data
761 operator
= sfile
.active_operator
763 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
765 def draw_header(self
, context
):
766 sfile
= context
.space_data
767 operator
= sfile
.active_operator
768 self
.layout
.prop(operator
, "export_animations", text
="")
770 def draw(self
, context
):
772 layout
.use_property_split
= True
773 layout
.use_property_decorate
= False # No animation.
775 sfile
= context
.space_data
776 operator
= sfile
.active_operator
778 layout
.active
= operator
.export_animations
780 layout
.prop(operator
, 'export_frame_range')
781 layout
.prop(operator
, 'export_frame_step')
782 layout
.prop(operator
, 'export_force_sampling')
783 layout
.prop(operator
, 'export_nla_strips')
786 row
.active
= operator
.export_force_sampling
787 row
.prop(operator
, 'export_def_bones')
790 class GLTF_PT_export_animation_shapekeys(bpy
.types
.Panel
):
791 bl_space_type
= 'FILE_BROWSER'
792 bl_region_type
= 'TOOL_PROPS'
793 bl_label
= "Shape Keys"
794 bl_parent_id
= "GLTF_PT_export_animation"
795 bl_options
= {'DEFAULT_CLOSED'}
798 def poll(cls
, context
):
799 sfile
= context
.space_data
800 operator
= sfile
.active_operator
802 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
804 def draw_header(self
, context
):
805 sfile
= context
.space_data
806 operator
= sfile
.active_operator
807 self
.layout
.prop(operator
, "export_morph", text
="")
809 def draw(self
, context
):
811 layout
.use_property_split
= True
812 layout
.use_property_decorate
= False # No animation.
814 sfile
= context
.space_data
815 operator
= sfile
.active_operator
817 layout
.active
= operator
.export_morph
819 layout
.prop(operator
, 'export_morph_normal')
820 col
= layout
.column()
821 col
.active
= operator
.export_morph_normal
822 col
.prop(operator
, 'export_morph_tangent')
825 class GLTF_PT_export_animation_skinning(bpy
.types
.Panel
):
826 bl_space_type
= 'FILE_BROWSER'
827 bl_region_type
= 'TOOL_PROPS'
828 bl_label
= "Skinning"
829 bl_parent_id
= "GLTF_PT_export_animation"
830 bl_options
= {'DEFAULT_CLOSED'}
833 def poll(cls
, context
):
834 sfile
= context
.space_data
835 operator
= sfile
.active_operator
837 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
839 def draw_header(self
, context
):
840 sfile
= context
.space_data
841 operator
= sfile
.active_operator
842 self
.layout
.prop(operator
, "export_skins", text
="")
844 def draw(self
, context
):
846 layout
.use_property_split
= True
847 layout
.use_property_decorate
= False # No animation.
849 sfile
= context
.space_data
850 operator
= sfile
.active_operator
852 layout
.active
= operator
.export_skins
853 layout
.prop(operator
, 'export_all_influences')
855 class GLTF_PT_export_user_extensions(bpy
.types
.Panel
):
856 bl_space_type
= 'FILE_BROWSER'
857 bl_region_type
= 'TOOL_PROPS'
858 bl_label
= "Extensions"
859 bl_parent_id
= "FILE_PT_operator"
860 bl_options
= {'DEFAULT_CLOSED'}
863 def poll(cls
, context
):
864 sfile
= context
.space_data
865 operator
= sfile
.active_operator
867 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf" and operator
.has_active_extenions
869 def draw(self
, context
):
871 layout
.use_property_split
= True
872 layout
.use_property_decorate
= False # No animation.
875 class ExportGLTF2(bpy
.types
.Operator
, ExportGLTF2_Base
, ExportHelper
):
876 """Export scene as glTF 2.0 file"""
877 bl_idname
= 'export_scene.gltf'
878 bl_label
= 'Export glTF 2.0'
882 filter_glob
: StringProperty(default
='*.glb;*.gltf', options
={'HIDDEN'})
885 def menu_func_export(self
, context
):
886 self
.layout
.operator(ExportGLTF2
.bl_idname
, text
='glTF 2.0 (.glb/.gltf)')
889 class ImportGLTF2(Operator
, ImportHelper
):
890 """Load a glTF 2.0 file"""
891 bl_idname
= 'import_scene.gltf'
892 bl_label
= 'Import glTF 2.0'
893 bl_options
= {'REGISTER', 'UNDO'}
895 filter_glob
: StringProperty(default
="*.glb;*.gltf", options
={'HIDDEN'})
897 files
: CollectionProperty(
899 type=bpy
.types
.OperatorFileListElement
,
902 loglevel
: IntProperty(
904 description
="Log Level")
906 import_pack_images
: BoolProperty(
908 description
='Pack all images into .blend file',
912 merge_vertices
: BoolProperty(
913 name
='Merge Vertices',
915 'The glTF format requires discontinuous normals, UVs, and '
916 'other vertex attributes to be stored as separate vertices, '
917 'as required for rendering on typical graphics hardware. '
918 'This option attempts to combine co-located vertices where possible. '
919 'Currently cannot combine verts with different normals'
924 import_shading
: EnumProperty(
926 items
=(("NORMALS", "Use Normal Data", ""),
927 ("FLAT", "Flat Shading", ""),
928 ("SMOOTH", "Smooth Shading", "")),
929 description
="How normals are computed during import",
932 bone_heuristic
: EnumProperty(
935 ("BLENDER", "Blender (best for re-importing)",
936 "Good for re-importing glTFs exported from Blender. "
937 "Bone tips are placed on their local +Y axis (in glTF space)"),
938 ("TEMPERANCE", "Temperance (average)",
939 "Decent all-around strategy. "
940 "A bone with one child has its tip placed on the local axis "
941 "closest to its child"),
942 ("FORTUNE", "Fortune (may look better, less accurate)",
943 "Might look better than Temperance, but also might have errors. "
944 "A bone with one child has its tip placed at its child's root. "
945 "Non-uniform scalings may get messed up though, so beware"),
947 description
="Heuristic for placing bones. Tries to make bones pretty",
948 default
="TEMPERANCE",
951 guess_original_bind_pose
: BoolProperty(
952 name
='Guess Original Bind Pose',
954 'Try to guess the original bind pose for skinned meshes from '
955 'the inverse bind matrices. '
956 'When off, use default/rest pose as bind pose'
961 def draw(self
, context
):
964 layout
.use_property_split
= True
965 layout
.use_property_decorate
= False # No animation.
967 layout
.prop(self
, 'import_pack_images')
968 layout
.prop(self
, 'merge_vertices')
969 layout
.prop(self
, 'import_shading')
970 layout
.prop(self
, 'guess_original_bind_pose')
971 layout
.prop(self
, 'bone_heuristic')
973 def execute(self
, context
):
974 return self
.import_gltf2(context
)
976 def import_gltf2(self
, context
):
980 import_settings
= self
.as_keywords()
983 # Multiple file import
985 dirname
= os
.path
.dirname(self
.filepath
)
986 for file in self
.files
:
987 path
= os
.path
.join(dirname
, file.name
)
988 if self
.unit_import(path
, import_settings
) == {'FINISHED'}:
993 return self
.unit_import(self
.filepath
, import_settings
)
995 def unit_import(self
, filename
, import_settings
):
997 from .io
.imp
.gltf2_io_gltf
import glTFImporter
, ImportError
998 from .blender
.imp
.gltf2_blender_gltf
import BlenderGlTF
1001 gltf_importer
= glTFImporter(filename
, import_settings
)
1002 gltf_importer
.read()
1003 gltf_importer
.checks()
1005 print("Data are loaded, start creating Blender stuff")
1007 start_time
= time
.time()
1008 BlenderGlTF
.create(gltf_importer
)
1009 elapsed_s
= "{:.2f}s".format(time
.time() - start_time
)
1010 print("glTF import finished in " + elapsed_s
)
1012 gltf_importer
.log
.removeHandler(gltf_importer
.log_handler
)
1016 except ImportError as e
:
1017 self
.report({'ERROR'}, e
.args
[0])
1018 return {'CANCELLED'}
1020 def set_debug_log(self
):
1022 if bpy
.app
.debug_value
== 0:
1023 self
.loglevel
= logging
.CRITICAL
1024 elif bpy
.app
.debug_value
== 1:
1025 self
.loglevel
= logging
.ERROR
1026 elif bpy
.app
.debug_value
== 2:
1027 self
.loglevel
= logging
.WARNING
1028 elif bpy
.app
.debug_value
== 3:
1029 self
.loglevel
= logging
.INFO
1031 self
.loglevel
= logging
.NOTSET
1034 def menu_func_import(self
, context
):
1035 self
.layout
.operator(ImportGLTF2
.bl_idname
, text
='glTF 2.0 (.glb/.gltf)')
1040 GLTF_PT_export_main
,
1041 GLTF_PT_export_include
,
1042 GLTF_PT_export_transform
,
1043 GLTF_PT_export_geometry
,
1044 GLTF_PT_export_geometry_compression
,
1045 GLTF_PT_export_animation
,
1046 GLTF_PT_export_animation_export
,
1047 GLTF_PT_export_animation_shapekeys
,
1048 GLTF_PT_export_animation_skinning
,
1049 GLTF_PT_export_user_extensions
,
1056 bpy
.utils
.register_class(c
)
1057 # bpy.utils.register_module(__name__)
1059 # add to the export / import menu
1060 bpy
.types
.TOPBAR_MT_file_export
.append(menu_func_export
)
1061 bpy
.types
.TOPBAR_MT_file_import
.append(menu_func_import
)
1066 bpy
.utils
.unregister_class(c
)
1067 for f
in extension_panel_unregister_functors
:
1069 extension_panel_unregister_functors
.clear()
1071 # bpy.utils.unregister_module(__name__)
1073 # remove from the export / import menu
1074 bpy
.types
.TOPBAR_MT_file_export
.remove(menu_func_export
)
1075 bpy
.types
.TOPBAR_MT_file_import
.remove(menu_func_import
)