Sun position: add time and place presets, remove reset operator
[blender-addons.git] / io_scene_gltf2 / __init__.py
blobe9a35a08b3afefbfd582f266fe89713c884cbad4
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.
15 bl_info = {
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',
18 "version": (1, 5, 1),
19 'blender': (2, 91, 0),
20 'location': 'File > Import-Export',
21 'description': 'Import-Export as glTF 2.0',
22 'warning': '',
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):
37 import importlib
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:
43 continue
45 if path.is_file() and path.suffix == ".py":
46 importlib.reload(module_dict[path.stem])
47 elif path.is_dir():
48 reload_package_recursive(path, module_dict[path.stem].__dict__)
50 reload_package_recursive(Path(__file__).parent, module_dict_main)
53 if "bpy" in locals():
54 reload_package(locals())
56 import bpy
57 from bpy.props import (StringProperty,
58 BoolProperty,
59 EnumProperty,
60 IntProperty,
61 CollectionProperty)
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":
78 return
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
91 def __init__(self):
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(
98 name='Format',
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')),
108 description=(
109 'Output format and embedding options. Binary is most efficient, '
110 'but JSON (embedded or separate) may be easier to edit later'
112 default='GLB',
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")),
121 name="ui_tab",
122 description="Export setting categories",
125 export_copyright: StringProperty(
126 name='Copyright',
127 description='Legal rights and conditions for the model',
128 default=''
131 export_image_format: EnumProperty(
132 name='Images',
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'),
140 description=(
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'
144 default='AUTO'
147 export_texture_dir: StringProperty(
148 name='Textures',
149 description='Folder to place texture files in. Relative to the .gltf file',
150 default='',
153 export_texcoords: BoolProperty(
154 name='UVs',
155 description='Export UVs (texture coordinates) with meshes',
156 default=True
159 export_normals: BoolProperty(
160 name='Normals',
161 description='Export vertex normals with meshes',
162 default=True
165 export_draco_mesh_compression_enable: BoolProperty(
166 name='Draco mesh compression',
167 description='Compress mesh using Draco',
168 default=False
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)',
174 default=6,
175 min=0,
176 max=6
179 export_draco_position_quantization: IntProperty(
180 name='Position quantization bits',
181 description='Quantization bits for position values (0 = no quantization)',
182 default=14,
183 min=0,
184 max=30
187 export_draco_normal_quantization: IntProperty(
188 name='Normal quantization bits',
189 description='Quantization bits for normal values (0 = no quantization)',
190 default=10,
191 min=0,
192 max=30
195 export_draco_texcoord_quantization: IntProperty(
196 name='Texcoord quantization bits',
197 description='Quantization bits for texture coordinate values (0 = no quantization)',
198 default=12,
199 min=0,
200 max=30
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)',
206 default=12,
207 min=0,
208 max=30
211 export_tangents: BoolProperty(
212 name='Tangents',
213 description='Export vertex tangents with meshes',
214 default=False
217 export_materials: EnumProperty(
218 name='Materials',
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 ',
226 default='EXPORT'
229 export_colors: BoolProperty(
230 name='Vertex Colors',
231 description='Export vertex colors with meshes',
232 default=True
235 export_cameras: BoolProperty(
236 name='Cameras',
237 description='Export cameras',
238 default=False
241 # keep it for compatibility (for now)
242 export_selected: BoolProperty(
243 name='Selected Objects',
244 description='Export selected objects only',
245 default=False
248 use_selection: BoolProperty(
249 name='Selected Objects',
250 description='Export selected objects only',
251 default=False
254 export_extras: BoolProperty(
255 name='Custom Properties',
256 description='Export custom properties as glTF extras',
257 default=False
260 export_yup: BoolProperty(
261 name='+Y Up',
262 description='Export using glTF convention, +Y up',
263 default=True
266 export_apply: BoolProperty(
267 name='Apply Modifiers',
268 description='Apply modifiers (excluding Armatures) to mesh objects -'
269 'WARNING: prevents exporting shape keys',
270 default=False
273 export_animations: BoolProperty(
274 name='Animations',
275 description='Exports active actions and NLA tracks as glTF animations',
276 default=True
279 export_frame_range: BoolProperty(
280 name='Limit to Playback Range',
281 description='Clips animations to selected playback range',
282 default=True
285 export_frame_step: IntProperty(
286 name='Sampling Rate',
287 description='How often to evaluate animated values (in frames)',
288 default=1,
289 min=1,
290 max=120
293 export_force_sampling: BoolProperty(
294 name='Always Sample Animations',
295 description='Apply sampling to all animations',
296 default=True
299 export_nla_strips: BoolProperty(
300 name='Group by NLA Track',
301 description=(
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"
306 default=True
309 export_def_bones: BoolProperty(
310 name='Export Deformation Bones Only',
311 description='Export Deformation bones only (and needed bones for hierarchy)',
312 default=False
315 export_current_frame: BoolProperty(
316 name='Use Current Frame',
317 description='Export the scene in the current animation frame',
318 default=False
321 export_skins: BoolProperty(
322 name='Skinning',
323 description='Export skinning (armature) data',
324 default=True
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',
330 default=False
333 export_morph: BoolProperty(
334 name='Shape Keys',
335 description='Export shape keys (morph targets)',
336 default=True
339 export_morph_normal: BoolProperty(
340 name='Shape Key Normals',
341 description='Export vertex normals with shape keys (morph targets)',
342 default=True
345 export_morph_tangent: BoolProperty(
346 name='Shape Key Tangents',
347 description='Export vertex tangents with shape keys (morph targets)',
348 default=False
351 export_lights: BoolProperty(
352 name='Punctual Lights',
353 description='Export directional, point, and spot lights. '
354 'Uses "KHR_lights_punctual" glTF extension',
355 default=False
358 export_displacement: BoolProperty(
359 name='Displacement Textures (EXPERIMENTAL)',
360 description='EXPERIMENTAL: Export displacement textures. '
361 'Uses incomplete "KHR_materials_displacement" glTF extension',
362 default=False
365 will_save_settings: BoolProperty(
366 name='Remember Export Settings',
367 description='Store glTF export settings in the Blender project',
368 default=False)
370 # Custom scene property for saving settings
371 scene_key = "glTF2ExportSettings"
375 def check(self, _context):
376 # Ensure file extension matches format
377 import os
378 filename = os.path.basename(self.filepath)
379 if filename:
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:
385 stem, ext = '', stem
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
396 return True
398 return False
400 def invoke(self, context, event):
401 settings = context.scene.get(self.scene_key)
402 self.will_save_settings = False
403 if settings:
404 try:
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)
408 del settings[k]
409 settings["use_selection"] = v
410 print("export_selected is now renamed use_selection, and will be deleted in a few release")
411 else:
412 setattr(self, k, v)
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]
419 import sys
420 preferences = bpy.context.preferences
421 for addon_name in preferences.addons.keys():
422 try:
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())
425 except Exception:
426 pass
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):
440 import os
441 import datetime
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.
450 export_settings = {}
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
475 else:
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
486 else:
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
501 else:
502 export_settings['gltf_def_bones'] = False
503 export_settings['gltf_nla_strips'] = self.export_nla_strips
504 else:
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
512 else:
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
518 else:
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
522 else:
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'
533 user_extensions = []
534 pre_export_callbacks = []
535 post_export_callbacks = []
537 import sys
538 preferences = bpy.context.preferences
539 for addon_name in preferences.addons.keys():
540 try:
541 module = sys.modules[addon_name]
542 except Exception:
543 continue
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'
568 bl_label = ""
569 bl_parent_id = "FILE_PT_operator"
570 bl_options = {'HIDE_HEADER'}
572 @classmethod
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):
580 layout = self.layout
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'
597 bl_label = "Include"
598 bl_parent_id = "FILE_PT_operator"
599 bl_options = {'DEFAULT_CLOSED'}
601 @classmethod
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):
609 layout = self.layout
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'}
632 @classmethod
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):
640 layout = self.layout
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'}
657 @classmethod
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):
665 layout = self.layout
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'}
692 def __init__(self):
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)
696 @classmethod
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):
709 layout = self.layout
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'}
733 @classmethod
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):
741 layout = self.layout
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'}
758 @classmethod
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):
771 layout = self.layout
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')
785 row = layout.row()
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'}
797 @classmethod
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):
810 layout = self.layout
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'}
832 @classmethod
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):
845 layout = self.layout
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'}
862 @classmethod
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):
870 layout = self.layout
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'
880 filename_ext = ''
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(
898 name="File Path",
899 type=bpy.types.OperatorFileListElement,
902 loglevel: IntProperty(
903 name='Log Level',
904 description="Log Level")
906 import_pack_images: BoolProperty(
907 name='Pack Images',
908 description='Pack all images into .blend file',
909 default=True
912 merge_vertices: BoolProperty(
913 name='Merge Vertices',
914 description=(
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'
921 default=False,
924 import_shading: EnumProperty(
925 name="Shading",
926 items=(("NORMALS", "Use Normal Data", ""),
927 ("FLAT", "Flat Shading", ""),
928 ("SMOOTH", "Smooth Shading", "")),
929 description="How normals are computed during import",
930 default="NORMALS")
932 bone_heuristic: EnumProperty(
933 name="Bone Dir",
934 items=(
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',
953 description=(
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'
958 default=True,
961 def draw(self, context):
962 layout = self.layout
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):
977 import os
979 self.set_debug_log()
980 import_settings = self.as_keywords()
982 if self.files:
983 # Multiple file import
984 ret = {'CANCELLED'}
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'}:
989 ret = {'FINISHED'}
990 return ret
991 else:
992 # Single file import
993 return self.unit_import(self.filepath, import_settings)
995 def unit_import(self, filename, import_settings):
996 import time
997 from .io.imp.gltf2_io_gltf import glTFImporter, ImportError
998 from .blender.imp.gltf2_blender_gltf import BlenderGlTF
1000 try:
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)
1014 return {'FINISHED'}
1016 except ImportError as e:
1017 self.report({'ERROR'}, e.args[0])
1018 return {'CANCELLED'}
1020 def set_debug_log(self):
1021 import logging
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
1030 else:
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)')
1038 classes = (
1039 ExportGLTF2,
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,
1050 ImportGLTF2
1054 def register():
1055 for c in classes:
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)
1064 def unregister():
1065 for c in classes:
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)