glTF export avoid crash when collision name in attributes
[blender-addons.git] / io_scene_gltf2 / __init__.py
blobbacd689d05473f91f9518b4c0549c7e48fd50759
1 # SPDX-License-Identifier: Apache-2.0
2 # Copyright 2018-2021 The glTF-Blender-IO authors.
4 bl_info = {
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',
7 "version": (3, 6, 18),
8 'blender': (3, 5, 0),
9 'location': 'File > Import-Export',
10 'description': 'Import-Export as glTF 2.0',
11 'warning': '',
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):
26 import importlib
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:
32 continue
34 if path.is_file() and path.suffix == ".py":
35 importlib.reload(module_dict[path.stem])
36 elif path.is_dir():
37 reload_package_recursive(path, module_dict[path.stem].__dict__)
39 reload_package_recursive(Path(__file__).parent, module_dict_main)
42 if "bpy" in locals():
43 reload_package(locals())
45 import bpy
46 from bpy.props import (StringProperty,
47 BoolProperty,
48 EnumProperty,
49 IntProperty,
50 CollectionProperty)
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):
64 import os
65 filename = os.path.basename(filepath)
66 if not filename:
67 return filepath
69 stem, ext = os.path.splitext(filename)
70 if stem.startswith('.') and not ext:
71 stem, ext = '', stem
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
80 else:
81 return filepath
84 def on_export_format_changed(self, context):
85 # Update the filename in the file browser when the format (.glb/.gltf)
86 # changes
87 sfile = context.space_data
88 if not isinstance(sfile, bpy.types.SpaceFileBrowser):
89 return
90 if not sfile.active_operator:
91 return
92 if sfile.active_operator.bl_idname != "EXPORT_SCENE_OT_gltf":
93 return
95 sfile.params.filename = ensure_filepath_matches_export_format(
96 sfile.params.filename,
97 self.export_format,
100 # Also change the filter
101 sfile.params.filter_glob = '*.glb' if self.export_format == 'GLB' else '*.gltf'
102 # Force update of file list, has update the filter does not update the real file list
103 bpy.ops.file.refresh()
106 class ConvertGLTF2_Base:
107 """Base class containing options that should be exposed during both import and export."""
109 export_import_convert_lighting_mode: EnumProperty(
110 name='Lighting Mode',
111 items=(
112 ('SPEC', 'Standard', 'Physically-based glTF lighting units (cd, lx, nt)'),
113 ('COMPAT', 'Unitless', 'Non-physical, unitless lighting. Useful when exposure controls are not available'),
114 ('RAW', 'Raw (Deprecated)', 'Blender lighting strengths with no conversion'),
116 description='Optional backwards compatibility for non-standard render engines. Applies to lights',# TODO: and emissive materials',
117 default='SPEC'
120 class ExportGLTF2_Base(ConvertGLTF2_Base):
121 # TODO: refactor to avoid boilerplate
123 def __init__(self):
124 from .io.com import gltf2_io_draco_compression_extension
125 self.is_draco_available = gltf2_io_draco_compression_extension.dll_exists()
127 bl_options = {'PRESET'}
129 # Don't use export_ prefix here, I don't want it to be saved with other export settings
130 gltf_export_id: StringProperty(
131 name='Identifier',
132 description=(
133 'Identifier of caller (in case of add-on calling this exporter). '
134 'Can be useful in case of Extension added by other add-ons'
136 default=''
139 export_format: EnumProperty(
140 name='Format',
141 items=(('GLB', 'glTF Binary (.glb)',
142 'Exports a single file, with all data packed in binary form. '
143 'Most efficient and portable, but more difficult to edit later'),
144 ('GLTF_SEPARATE', 'glTF Separate (.gltf + .bin + textures)',
145 'Exports multiple files, with separate JSON, binary and texture data. '
146 'Easiest to edit later'),
147 ('GLTF_EMBEDDED', 'glTF Embedded (.gltf)',
148 'Exports a single file, with all data packed in JSON. '
149 'Less efficient than binary, but easier to edit later')),
150 description=(
151 'Output format and embedding options. Binary is most efficient, '
152 'but JSON (embedded or separate) may be easier to edit later'
154 default='GLB', #Warning => If you change the default, need to change the default filter too
155 update=on_export_format_changed,
158 ui_tab: EnumProperty(
159 items=(('GENERAL', "General", "General settings"),
160 ('MESHES', "Meshes", "Mesh settings"),
161 ('OBJECTS', "Objects", "Object settings"),
162 ('ANIMATION', "Animation", "Animation settings")),
163 name="ui_tab",
164 description="Export setting categories",
167 export_copyright: StringProperty(
168 name='Copyright',
169 description='Legal rights and conditions for the model',
170 default=''
173 export_image_format: EnumProperty(
174 name='Images',
175 items=(('AUTO', 'Automatic',
176 'Save PNGs as PNGs and JPEGs as JPEGs. '
177 'If neither one, use PNG'),
178 ('JPEG', 'JPEG Format (.jpg)',
179 'Save images as JPEGs. (Images that need alpha are saved as PNGs though.) '
180 'Be aware of a possible loss in quality'),
181 ('NONE', 'None',
182 'Don\'t export images'),
184 description=(
185 'Output format for images. PNG is lossless and generally preferred, but JPEG might be preferable for web '
186 'applications due to the smaller file size. Alternatively they can be omitted if they are not needed'
188 default='AUTO'
191 export_texture_dir: StringProperty(
192 name='Textures',
193 description='Folder to place texture files in. Relative to the .gltf file',
194 default='',
197 export_jpeg_quality: IntProperty(
198 name='JPEG quality',
199 description='Quality of JPEG export',
200 default=75,
201 min=0,
202 max=100
205 export_keep_originals: BoolProperty(
206 name='Keep original',
207 description=('Keep original textures files if possible. '
208 'WARNING: if you use more than one texture, '
209 'where pbr standard requires only one, only one texture will be used. '
210 'This can lead to unexpected results'
212 default=False,
215 export_texcoords: BoolProperty(
216 name='UVs',
217 description='Export UVs (texture coordinates) with meshes',
218 default=True
221 export_normals: BoolProperty(
222 name='Normals',
223 description='Export vertex normals with meshes',
224 default=True
227 export_draco_mesh_compression_enable: BoolProperty(
228 name='Draco mesh compression',
229 description='Compress mesh using Draco',
230 default=False
233 export_draco_mesh_compression_level: IntProperty(
234 name='Compression level',
235 description='Compression level (0 = most speed, 6 = most compression, higher values currently not supported)',
236 default=6,
237 min=0,
238 max=10
241 export_draco_position_quantization: IntProperty(
242 name='Position quantization bits',
243 description='Quantization bits for position values (0 = no quantization)',
244 default=14,
245 min=0,
246 max=30
249 export_draco_normal_quantization: IntProperty(
250 name='Normal quantization bits',
251 description='Quantization bits for normal values (0 = no quantization)',
252 default=10,
253 min=0,
254 max=30
257 export_draco_texcoord_quantization: IntProperty(
258 name='Texcoord quantization bits',
259 description='Quantization bits for texture coordinate values (0 = no quantization)',
260 default=12,
261 min=0,
262 max=30
265 export_draco_color_quantization: IntProperty(
266 name='Color quantization bits',
267 description='Quantization bits for color values (0 = no quantization)',
268 default=10,
269 min=0,
270 max=30
273 export_draco_generic_quantization: IntProperty(
274 name='Generic quantization bits',
275 description='Quantization bits for generic values like weights or joints (0 = no quantization)',
276 default=12,
277 min=0,
278 max=30
281 export_tangents: BoolProperty(
282 name='Tangents',
283 description='Export vertex tangents with meshes',
284 default=False
287 export_materials: EnumProperty(
288 name='Materials',
289 items=(('EXPORT', 'Export',
290 'Export all materials used by included objects'),
291 ('PLACEHOLDER', 'Placeholder',
292 'Do not export materials, but write multiple primitive groups per mesh, keeping material slot information'),
293 ('NONE', 'No export',
294 'Do not export materials, and combine mesh primitive groups, losing material slot information')),
295 description='Export materials',
296 default='EXPORT'
299 export_original_specular: BoolProperty(
300 name='Export original PBR Specular',
301 description=(
302 'Export original glTF PBR Specular, instead of Blender Principled Shader Specular'
304 default=False,
307 export_colors: BoolProperty(
308 name='Vertex Colors',
309 description='Export vertex colors with meshes',
310 default=True
313 export_attributes: BoolProperty(
314 name='Attributes',
315 description='Export Attributes (when starting with underscore)',
316 default=False
319 use_mesh_edges: BoolProperty(
320 name='Loose Edges',
321 description=(
322 'Export loose edges as lines, using the material from the first material slot'
324 default=False,
327 use_mesh_vertices: BoolProperty(
328 name='Loose Points',
329 description=(
330 'Export loose points as glTF points, using the material from the first material slot'
332 default=False,
335 export_cameras: BoolProperty(
336 name='Cameras',
337 description='Export cameras',
338 default=False
341 use_selection: BoolProperty(
342 name='Selected Objects',
343 description='Export selected objects only',
344 default=False
347 use_visible: BoolProperty(
348 name='Visible Objects',
349 description='Export visible objects only',
350 default=False
353 use_renderable: BoolProperty(
354 name='Renderable Objects',
355 description='Export renderable objects only',
356 default=False
359 use_active_collection_with_nested: BoolProperty(
360 name='Include Nested Collections',
361 description='Include active collection and nested collections',
362 default=True
365 use_active_collection: BoolProperty(
366 name='Active Collection',
367 description='Export objects in the active collection only',
368 default=False
371 use_active_scene: BoolProperty(
372 name='Active Scene',
373 description='Export active scene only',
374 default=False
377 export_extras: BoolProperty(
378 name='Custom Properties',
379 description='Export custom properties as glTF extras',
380 default=False
383 export_yup: BoolProperty(
384 name='+Y Up',
385 description='Export using glTF convention, +Y up',
386 default=True
389 export_apply: BoolProperty(
390 name='Apply Modifiers',
391 description='Apply modifiers (excluding Armatures) to mesh objects -'
392 'WARNING: prevents exporting shape keys',
393 default=False
396 export_animations: BoolProperty(
397 name='Animations',
398 description='Exports active actions and NLA tracks as glTF animations',
399 default=True
402 export_frame_range: BoolProperty(
403 name='Limit to Playback Range',
404 description='Clips animations to selected playback range',
405 default=False
408 export_frame_step: IntProperty(
409 name='Sampling Rate',
410 description='How often to evaluate animated values (in frames)',
411 default=1,
412 min=1,
413 max=120
416 export_force_sampling: BoolProperty(
417 name='Always Sample Animations',
418 description='Apply sampling to all animations',
419 default=True
422 export_animation_mode: EnumProperty(
423 name='Animation mode',
424 items=(('ACTIONS', 'Actions',
425 'Export actions (actives and on NLA tracks) as separate animations'),
426 ('ACTIVE_ACTIONS', 'Active actions merged',
427 'All the currently assigned actions become one glTF animation'),
428 ('NLA_TRACKS', 'NLA Tracks',
429 'Export individual NLA Tracks as separate animation'),
430 ('SCENE', 'Scene',
431 'Export baked scene as a single animation')
433 description='Export Animation mode',
434 default='ACTIONS'
437 export_nla_strips_merged_animation_name: StringProperty(
438 name='Merged Animation Name',
439 description=(
440 "Name of single glTF animation to be exported"
442 default='Animation'
445 export_def_bones: BoolProperty(
446 name='Export Deformation Bones Only',
447 description='Export Deformation bones only',
448 default=False
451 export_hierarchy_flatten_bones: BoolProperty(
452 name='Flatten Bone Hierarchy',
453 description='Flatten Bone Hierarchy. Useful in case of non decomposable transformation matrix',
454 default=False
457 export_optimize_animation_size: BoolProperty(
458 name='Optimize Animation Size',
459 description=(
460 "Reduce exported file size by removing duplicate keyframes"
462 default=True
465 export_optimize_animation_keep_anim_armature: BoolProperty(
466 name='Force keeping channels for bones',
467 description=(
468 "if all keyframes are identical in a rig, "
469 "force keeping the minimal animation. "
470 "When off, all possible channels for "
471 "the bones will be exported, even if empty "
472 "(minimal animation, 2 keyframes)"
474 default=True
477 export_optimize_animation_keep_anim_object: BoolProperty(
478 name='Force keeping channel for objects',
479 description=(
480 "If all keyframes are identical for object transformations, "
481 "force keeping the minimal animation"
483 default=False
486 export_negative_frame: EnumProperty(
487 name='Negative Frames',
488 items=(('SLIDE', 'Slide',
489 'Slide animation to start at frame 0'),
490 ('CROP', 'Crop',
491 'Keep only frames above frame 0'),
493 description='Negative Frames are slid or cropped',
494 default='SLIDE'
497 export_anim_slide_to_zero: BoolProperty(
498 name='Set all glTF Animation starting at 0',
499 description=(
500 "Set all glTF animation starting at 0.0s. "
501 "Can be useful for looping animations"
503 default=False
506 export_bake_animation: BoolProperty(
507 name='Bake All Objects Animations',
508 description=(
509 "Force exporting animation on every objects. "
510 "Can be useful when using constraints or driver. "
511 "Also useful when exporting only selection"
513 default=False
516 export_anim_single_armature: BoolProperty(
517 name='Export all Armature Actions',
518 description=(
519 "Export all actions, bound to a single armature. "
520 "WARNING: Option does not support exports including multiple armatures"
522 default=True
525 export_reset_pose_bones: BoolProperty(
526 name='Reset pose bones between actions',
527 description=(
528 "Reset pose bones between each action exported. "
529 "This is needed when some bones are not keyed on some animations"
531 default=True
534 export_current_frame: BoolProperty(
535 name='Use Current Frame as Object Rest Transformations',
536 description=(
537 'Export the scene in the current animation frame. '
538 'When off, frame O is used as rest transformations for objects'
540 default=False
543 export_rest_position_armature: BoolProperty(
544 name='Use Rest Position Armature',
545 description=(
546 "Export armatures using rest position as joins rest pose. "
547 "When off, current frame pose is used as rest pose"
549 default=True
552 export_anim_scene_split_object: BoolProperty(
553 name='Split Animation by Object',
554 description=(
555 "Export Scene as seen in Viewport, "
556 "But split animation by Object"
558 default=True
561 export_skins: BoolProperty(
562 name='Skinning',
563 description='Export skinning (armature) data',
564 default=True
567 export_all_influences: BoolProperty(
568 name='Include All Bone Influences',
569 description='Allow >4 joint vertex influences. Models may appear incorrectly in many viewers',
570 default=False
573 export_morph: BoolProperty(
574 name='Shape Keys',
575 description='Export shape keys (morph targets)',
576 default=True
579 export_morph_normal: BoolProperty(
580 name='Shape Key Normals',
581 description='Export vertex normals with shape keys (morph targets)',
582 default=True
585 export_morph_tangent: BoolProperty(
586 name='Shape Key Tangents',
587 description='Export vertex tangents with shape keys (morph targets)',
588 default=False
591 export_morph_animation: BoolProperty(
592 name='Shape Key Animations',
593 description='Export shape keys animations (morph targets)',
594 default=True
597 export_lights: BoolProperty(
598 name='Punctual Lights',
599 description='Export directional, point, and spot lights. '
600 'Uses "KHR_lights_punctual" glTF extension',
601 default=False
604 will_save_settings: BoolProperty(
605 name='Remember Export Settings',
606 description='Store glTF export settings in the Blender project',
607 default=False)
609 # Custom scene property for saving settings
610 scene_key = "glTF2ExportSettings"
614 def check(self, _context):
615 # Ensure file extension matches format
616 old_filepath = self.filepath
617 self.filepath = ensure_filepath_matches_export_format(
618 self.filepath,
619 self.export_format,
621 return self.filepath != old_filepath
623 def invoke(self, context, event):
624 settings = context.scene.get(self.scene_key)
625 self.will_save_settings = False
626 if settings:
627 try:
628 for (k, v) in settings.items():
629 setattr(self, k, v)
630 self.will_save_settings = True
632 except (AttributeError, TypeError):
633 self.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings")
634 del context.scene[self.scene_key]
636 import sys
637 preferences = bpy.context.preferences
638 for addon_name in preferences.addons.keys():
639 try:
640 if hasattr(sys.modules[addon_name], 'glTF2ExportUserExtension') or hasattr(sys.modules[addon_name], 'glTF2ExportUserExtensions'):
641 exporter_extension_panel_unregister_functors.append(sys.modules[addon_name].register_panel())
642 except Exception:
643 pass
645 self.has_active_exporter_extensions = len(exporter_extension_panel_unregister_functors) > 0
646 return ExportHelper.invoke(self, context, event)
648 def save_settings(self, context):
649 # find all props to save
650 exceptional = [
651 # options that don't start with 'export_'
652 'use_selection',
653 'use_visible',
654 'use_renderable',
655 'use_active_collection_with_nested',
656 'use_active_collection',
657 'use_mesh_edges',
658 'use_mesh_vertices',
659 'use_active_scene',
661 all_props = self.properties
662 export_props = {
663 x: getattr(self, x) for x in dir(all_props)
664 if (x.startswith("export_") or x in exceptional) and all_props.get(x) is not None
666 context.scene[self.scene_key] = export_props
668 def execute(self, context):
669 import os
670 import datetime
671 from .blender.exp import gltf2_blender_export
672 from .io.com.gltf2_io_path import path_to_uri
674 if self.will_save_settings:
675 self.save_settings(context)
677 self.check(context) # ensure filepath has the right extension
679 # All custom export settings are stored in this container.
680 export_settings = {}
682 export_settings['timestamp'] = datetime.datetime.now()
683 export_settings['gltf_export_id'] = self.gltf_export_id
684 export_settings['gltf_filepath'] = self.filepath
685 export_settings['gltf_filedirectory'] = os.path.dirname(export_settings['gltf_filepath']) + '/'
686 export_settings['gltf_texturedirectory'] = os.path.join(
687 export_settings['gltf_filedirectory'],
688 self.export_texture_dir,
690 export_settings['gltf_keep_original_textures'] = self.export_keep_originals
692 export_settings['gltf_format'] = self.export_format
693 export_settings['gltf_image_format'] = self.export_image_format
694 export_settings['gltf_jpeg_quality'] = self.export_jpeg_quality
695 export_settings['gltf_copyright'] = self.export_copyright
696 export_settings['gltf_texcoords'] = self.export_texcoords
697 export_settings['gltf_normals'] = self.export_normals
698 export_settings['gltf_tangents'] = self.export_tangents and self.export_normals
699 export_settings['gltf_loose_edges'] = self.use_mesh_edges
700 export_settings['gltf_loose_points'] = self.use_mesh_vertices
702 if self.is_draco_available:
703 export_settings['gltf_draco_mesh_compression'] = self.export_draco_mesh_compression_enable
704 export_settings['gltf_draco_mesh_compression_level'] = self.export_draco_mesh_compression_level
705 export_settings['gltf_draco_position_quantization'] = self.export_draco_position_quantization
706 export_settings['gltf_draco_normal_quantization'] = self.export_draco_normal_quantization
707 export_settings['gltf_draco_texcoord_quantization'] = self.export_draco_texcoord_quantization
708 export_settings['gltf_draco_color_quantization'] = self.export_draco_color_quantization
709 export_settings['gltf_draco_generic_quantization'] = self.export_draco_generic_quantization
710 else:
711 export_settings['gltf_draco_mesh_compression'] = False
713 export_settings['gltf_materials'] = self.export_materials
714 export_settings['gltf_colors'] = self.export_colors
715 export_settings['gltf_attributes'] = self.export_attributes
716 export_settings['gltf_cameras'] = self.export_cameras
718 export_settings['gltf_original_specular'] = self.export_original_specular
720 export_settings['gltf_visible'] = self.use_visible
721 export_settings['gltf_renderable'] = self.use_renderable
723 export_settings['gltf_active_collection'] = self.use_active_collection
724 if self.use_active_collection:
725 export_settings['gltf_active_collection_with_nested'] = self.use_active_collection_with_nested
726 else:
727 export_settings['gltf_active_collection_with_nested'] = False
728 export_settings['gltf_active_scene'] = self.use_active_scene
730 export_settings['gltf_selected'] = self.use_selection
731 export_settings['gltf_layers'] = True # self.export_layers
732 export_settings['gltf_extras'] = self.export_extras
733 export_settings['gltf_yup'] = self.export_yup
734 export_settings['gltf_apply'] = self.export_apply
735 export_settings['gltf_current_frame'] = self.export_current_frame
736 export_settings['gltf_animations'] = self.export_animations
737 export_settings['gltf_def_bones'] = self.export_def_bones
738 export_settings['gltf_flatten_bones_hierarchy'] = self.export_hierarchy_flatten_bones
739 if self.export_animations:
740 export_settings['gltf_frame_range'] = self.export_frame_range
741 export_settings['gltf_force_sampling'] = self.export_force_sampling
742 if not self.export_force_sampling:
743 export_settings['gltf_def_bones'] = False
744 export_settings['gltf_bake_animation'] = False
745 export_settings['gltf_animation_mode'] = self.export_animation_mode
746 if export_settings['gltf_animation_mode'] == "NLA_TRACKS":
747 export_settings['gltf_force_sampling'] = True
748 if export_settings['gltf_animation_mode'] == "SCENE":
749 export_settings['gltf_anim_scene_split_object'] = self.export_anim_scene_split_object
750 else:
751 export_settings['gltf_anim_scene_split_object'] = False
753 export_settings['gltf_nla_strips_merged_animation_name'] = self.export_nla_strips_merged_animation_name
754 export_settings['gltf_optimize_animation'] = self.export_optimize_animation_size
755 export_settings['gltf_optimize_animation_keep_armature'] = self.export_optimize_animation_keep_anim_armature
756 export_settings['gltf_optimize_animation_keep_object'] = self.export_optimize_animation_keep_anim_object
757 export_settings['gltf_export_anim_single_armature'] = self.export_anim_single_armature
758 export_settings['gltf_export_reset_pose_bones'] = self.export_reset_pose_bones
759 export_settings['gltf_bake_animation'] = self.export_bake_animation
760 export_settings['gltf_negative_frames'] = self.export_negative_frame
761 export_settings['gltf_anim_slide_to_zero'] = self.export_anim_slide_to_zero
762 else:
763 export_settings['gltf_frame_range'] = False
764 export_settings['gltf_force_sampling'] = False
765 export_settings['gltf_bake_animation'] = False
766 export_settings['gltf_optimize_animation'] = False
767 export_settings['gltf_optimize_animation_keep_armature'] = False
768 export_settings['gltf_optimize_animation_keep_object'] = False
769 export_settings['gltf_export_anim_single_armature'] = False
770 export_settings['gltf_export_reset_pose_bones'] = False
771 export_settings['gltf_skins'] = self.export_skins
772 if self.export_skins:
773 export_settings['gltf_all_vertex_influences'] = self.export_all_influences
774 else:
775 export_settings['gltf_all_vertex_influences'] = False
776 export_settings['gltf_def_bones'] = False
777 export_settings['gltf_rest_position_armature'] = self.export_rest_position_armature
778 export_settings['gltf_frame_step'] = self.export_frame_step
780 export_settings['gltf_morph'] = self.export_morph
781 if self.export_morph:
782 export_settings['gltf_morph_normal'] = self.export_morph_normal
783 export_settings['gltf_morph_tangent'] = self.export_morph_tangent
784 export_settings['gltf_morph_anim'] = self.export_morph_animation
785 else:
786 export_settings['gltf_morph_normal'] = False
787 export_settings['gltf_morph_tangent'] = False
788 export_settings['gltf_morph_anim'] = False
790 export_settings['gltf_lights'] = self.export_lights
791 export_settings['gltf_lighting_mode'] = self.export_import_convert_lighting_mode
793 export_settings['gltf_binary'] = bytearray()
794 export_settings['gltf_binaryfilename'] = (
795 path_to_uri(os.path.splitext(os.path.basename(self.filepath))[0] + '.bin')
798 user_extensions = []
799 pre_export_callbacks = []
800 post_export_callbacks = []
802 import sys
803 preferences = bpy.context.preferences
804 for addon_name in preferences.addons.keys():
805 try:
806 module = sys.modules[addon_name]
807 except Exception:
808 continue
809 if hasattr(module, 'glTF2ExportUserExtension'):
810 extension_ctor = module.glTF2ExportUserExtension
811 user_extensions.append(extension_ctor())
812 if hasattr(module, 'glTF2ExportUserExtensions'):
813 extension_ctors = module.glTF2ExportUserExtensions
814 for extension_ctor in extension_ctors:
815 user_extensions.append(extension_ctor())
816 if hasattr(module, 'glTF2_pre_export_callback'):
817 pre_export_callbacks.append(module.glTF2_pre_export_callback)
818 if hasattr(module, 'glTF2_post_export_callback'):
819 post_export_callbacks.append(module.glTF2_post_export_callback)
820 export_settings['gltf_user_extensions'] = user_extensions
821 export_settings['pre_export_callbacks'] = pre_export_callbacks
822 export_settings['post_export_callbacks'] = post_export_callbacks
824 return gltf2_blender_export.save(context, export_settings)
826 def draw(self, context):
827 pass # Is needed to get panels available
830 class GLTF_PT_export_main(bpy.types.Panel):
831 bl_space_type = 'FILE_BROWSER'
832 bl_region_type = 'TOOL_PROPS'
833 bl_label = ""
834 bl_parent_id = "FILE_PT_operator"
835 bl_options = {'HIDE_HEADER'}
837 @classmethod
838 def poll(cls, context):
839 sfile = context.space_data
840 operator = sfile.active_operator
842 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
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.prop(operator, 'export_format')
853 if operator.export_format == 'GLTF_SEPARATE':
854 layout.prop(operator, 'export_keep_originals')
855 if operator.export_keep_originals is False:
856 layout.prop(operator, 'export_texture_dir', icon='FILE_FOLDER')
858 layout.prop(operator, 'export_copyright')
859 layout.prop(operator, 'will_save_settings')
862 class GLTF_PT_export_include(bpy.types.Panel):
863 bl_space_type = 'FILE_BROWSER'
864 bl_region_type = 'TOOL_PROPS'
865 bl_label = "Include"
866 bl_parent_id = "FILE_PT_operator"
867 bl_options = {'DEFAULT_CLOSED'}
869 @classmethod
870 def poll(cls, context):
871 sfile = context.space_data
872 operator = sfile.active_operator
874 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
876 def draw(self, context):
877 layout = self.layout
878 layout.use_property_split = True
879 layout.use_property_decorate = False # No animation.
881 sfile = context.space_data
882 operator = sfile.active_operator
884 col = layout.column(heading = "Limit to", align = True)
885 col.prop(operator, 'use_selection')
886 col.prop(operator, 'use_visible')
887 col.prop(operator, 'use_renderable')
888 col.prop(operator, 'use_active_collection')
889 if operator.use_active_collection:
890 col.prop(operator, 'use_active_collection_with_nested')
891 col.prop(operator, 'use_active_scene')
893 col = layout.column(heading = "Data", align = True)
894 col.prop(operator, 'export_extras')
895 col.prop(operator, 'export_cameras')
896 col.prop(operator, 'export_lights')
899 class GLTF_PT_export_transform(bpy.types.Panel):
900 bl_space_type = 'FILE_BROWSER'
901 bl_region_type = 'TOOL_PROPS'
902 bl_label = "Transform"
903 bl_parent_id = "FILE_PT_operator"
904 bl_options = {'DEFAULT_CLOSED'}
906 @classmethod
907 def poll(cls, context):
908 sfile = context.space_data
909 operator = sfile.active_operator
911 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
913 def draw(self, context):
914 layout = self.layout
915 layout.use_property_split = True
916 layout.use_property_decorate = False # No animation.
918 sfile = context.space_data
919 operator = sfile.active_operator
921 layout.prop(operator, 'export_yup')
924 class GLTF_PT_export_data(bpy.types.Panel):
925 bl_space_type = 'FILE_BROWSER'
926 bl_region_type = 'TOOL_PROPS'
927 bl_label = "Data"
928 bl_parent_id = "FILE_PT_operator"
929 bl_options = {'DEFAULT_CLOSED'}
931 @classmethod
932 def poll(cls, context):
933 sfile = context.space_data
934 operator = sfile.active_operator
936 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
938 def draw(self, context):
939 pass
941 class GLTF_PT_export_data_mesh(bpy.types.Panel):
942 bl_space_type = 'FILE_BROWSER'
943 bl_region_type = 'TOOL_PROPS'
944 bl_label = "Mesh"
945 bl_parent_id = "GLTF_PT_export_data"
946 bl_options = {'DEFAULT_CLOSED'}
948 @classmethod
949 def poll(cls, context):
950 sfile = context.space_data
951 operator = sfile.active_operator
952 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
954 def draw(self, context):
955 layout = self.layout
956 layout.use_property_split = True
957 layout.use_property_decorate = False # No animation.
959 sfile = context.space_data
960 operator = sfile.active_operator
962 layout.prop(operator, 'export_apply')
963 layout.prop(operator, 'export_texcoords')
964 layout.prop(operator, 'export_normals')
965 col = layout.column()
966 col.active = operator.export_normals
967 col.prop(operator, 'export_tangents')
968 layout.prop(operator, 'export_colors')
969 layout.prop(operator, 'export_attributes')
971 col = layout.column()
972 col.prop(operator, 'use_mesh_edges')
973 col.prop(operator, 'use_mesh_vertices')
976 class GLTF_PT_export_data_material(bpy.types.Panel):
977 bl_space_type = 'FILE_BROWSER'
978 bl_region_type = 'TOOL_PROPS'
979 bl_label = "Material"
980 bl_parent_id = "GLTF_PT_export_data"
981 bl_options = {'DEFAULT_CLOSED'}
983 @classmethod
984 def poll(cls, context):
985 sfile = context.space_data
986 operator = sfile.active_operator
987 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
989 def draw(self, context):
990 layout = self.layout
991 layout.use_property_split = True
992 layout.use_property_decorate = False # No animation.
994 sfile = context.space_data
995 operator = sfile.active_operator
997 layout.prop(operator, 'export_materials')
998 col = layout.column()
999 col.active = operator.export_materials == "EXPORT"
1000 col.prop(operator, 'export_image_format')
1001 if operator.export_image_format in ["AUTO", "JPEG"]:
1002 col.prop(operator, 'export_jpeg_quality')
1004 class GLTF_PT_export_data_original_pbr(bpy.types.Panel):
1005 bl_space_type = 'FILE_BROWSER'
1006 bl_region_type = 'TOOL_PROPS'
1007 bl_label = "PBR Extensions"
1008 bl_parent_id = "GLTF_PT_export_data_material"
1009 bl_options = {'DEFAULT_CLOSED'}
1011 @classmethod
1012 def poll(cls, context):
1013 sfile = context.space_data
1014 operator = sfile.active_operator
1015 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1017 def draw(self, context):
1018 layout = self.layout
1019 layout.use_property_split = True
1020 layout.use_property_decorate = False # No animation.
1022 sfile = context.space_data
1023 operator = sfile.active_operator
1025 layout.prop(operator, 'export_original_specular')
1027 class GLTF_PT_export_data_lighting(bpy.types.Panel):
1028 bl_space_type = 'FILE_BROWSER'
1029 bl_region_type = 'TOOL_PROPS'
1030 bl_label = "Lighting"
1031 bl_parent_id = "GLTF_PT_export_data"
1032 bl_options = {'DEFAULT_CLOSED'}
1034 @classmethod
1035 def poll(cls, context):
1036 sfile = context.space_data
1037 operator = sfile.active_operator
1038 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1040 def draw(self, context):
1041 layout = self.layout
1042 layout.use_property_split = True
1043 layout.use_property_decorate = False # No animation.
1045 sfile = context.space_data
1046 operator = sfile.active_operator
1048 layout.prop(operator, 'export_import_convert_lighting_mode')
1050 class GLTF_PT_export_data_shapekeys(bpy.types.Panel):
1051 bl_space_type = 'FILE_BROWSER'
1052 bl_region_type = 'TOOL_PROPS'
1053 bl_label = "Shape Keys"
1054 bl_parent_id = "GLTF_PT_export_data"
1055 bl_options = {'DEFAULT_CLOSED'}
1057 @classmethod
1058 def poll(cls, context):
1059 sfile = context.space_data
1060 operator = sfile.active_operator
1062 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1064 def draw_header(self, context):
1065 sfile = context.space_data
1066 operator = sfile.active_operator
1067 self.layout.prop(operator, "export_morph", text="")
1069 def draw(self, context):
1070 layout = self.layout
1071 layout.use_property_split = True
1072 layout.use_property_decorate = False # No animation.
1074 sfile = context.space_data
1075 operator = sfile.active_operator
1077 layout.active = operator.export_morph
1079 layout.prop(operator, 'export_morph_normal')
1080 col = layout.column()
1081 col.active = operator.export_morph_normal
1082 col.prop(operator, 'export_morph_tangent')
1085 class GLTF_PT_export_data_skinning(bpy.types.Panel):
1086 bl_space_type = 'FILE_BROWSER'
1087 bl_region_type = 'TOOL_PROPS'
1088 bl_label = "Skinning"
1089 bl_parent_id = "GLTF_PT_export_data"
1090 bl_options = {'DEFAULT_CLOSED'}
1092 @classmethod
1093 def poll(cls, context):
1094 sfile = context.space_data
1095 operator = sfile.active_operator
1097 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1099 def draw_header(self, context):
1100 sfile = context.space_data
1101 operator = sfile.active_operator
1102 self.layout.prop(operator, "export_skins", text="")
1104 def draw(self, context):
1105 layout = self.layout
1106 layout.use_property_split = True
1107 layout.use_property_decorate = False # No animation.
1109 sfile = context.space_data
1110 operator = sfile.active_operator
1112 layout.active = operator.export_skins
1114 layout.prop(operator, 'export_all_influences')
1117 class GLTF_PT_export_data_armature(bpy.types.Panel):
1118 bl_space_type = 'FILE_BROWSER'
1119 bl_region_type = 'TOOL_PROPS'
1120 bl_label = "Armature"
1121 bl_parent_id = "GLTF_PT_export_data"
1122 bl_options = {'DEFAULT_CLOSED'}
1124 @classmethod
1125 def poll(cls, context):
1126 sfile = context.space_data
1127 operator = sfile.active_operator
1129 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1131 def draw(self, context):
1132 layout = self.layout
1133 layout.use_property_split = True
1134 layout.use_property_decorate = False # No animation.
1136 sfile = context.space_data
1137 operator = sfile.active_operator
1139 layout.active = operator.export_skins
1141 layout.prop(operator, 'export_rest_position_armature')
1143 row = layout.row()
1144 row.active = operator.export_force_sampling
1145 row.prop(operator, 'export_def_bones')
1146 if operator.export_force_sampling is False and operator.export_def_bones is True:
1147 layout.label(text="Export only deformation bones is not possible when not sampling animation")
1148 row = layout.row()
1149 row.prop(operator, 'export_hierarchy_flatten_bones')
1151 class GLTF_PT_export_data_compression(bpy.types.Panel):
1152 bl_space_type = 'FILE_BROWSER'
1153 bl_region_type = 'TOOL_PROPS'
1154 bl_label = "Compression"
1155 bl_parent_id = "GLTF_PT_export_data"
1156 bl_options = {'DEFAULT_CLOSED'}
1158 def __init__(self):
1159 from .io.com import gltf2_io_draco_compression_extension
1160 self.is_draco_available = gltf2_io_draco_compression_extension.dll_exists(quiet=True)
1162 @classmethod
1163 def poll(cls, context):
1164 sfile = context.space_data
1165 operator = sfile.active_operator
1166 if operator.is_draco_available:
1167 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1169 def draw_header(self, context):
1170 sfile = context.space_data
1171 operator = sfile.active_operator
1172 self.layout.prop(operator, "export_draco_mesh_compression_enable", text="")
1174 def draw(self, context):
1175 layout = self.layout
1176 layout.use_property_split = True
1177 layout.use_property_decorate = False # No animation.
1179 sfile = context.space_data
1180 operator = sfile.active_operator
1182 layout.active = operator.export_draco_mesh_compression_enable
1183 layout.prop(operator, 'export_draco_mesh_compression_level')
1185 col = layout.column(align=True)
1186 col.prop(operator, 'export_draco_position_quantization', text="Quantize Position")
1187 col.prop(operator, 'export_draco_normal_quantization', text="Normal")
1188 col.prop(operator, 'export_draco_texcoord_quantization', text="Tex Coord")
1189 col.prop(operator, 'export_draco_color_quantization', text="Color")
1190 col.prop(operator, 'export_draco_generic_quantization', text="Generic")
1193 class GLTF_PT_export_animation(bpy.types.Panel):
1194 bl_space_type = 'FILE_BROWSER'
1195 bl_region_type = 'TOOL_PROPS'
1196 bl_label = "Animation"
1197 bl_parent_id = "FILE_PT_operator"
1198 bl_options = {'DEFAULT_CLOSED'}
1200 @classmethod
1201 def poll(cls, context):
1202 sfile = context.space_data
1203 operator = sfile.active_operator
1205 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1207 def draw_header(self, context):
1208 sfile = context.space_data
1209 operator = sfile.active_operator
1210 self.layout.prop(operator, "export_animations", text="")
1212 def draw(self, context):
1213 layout = self.layout
1214 layout.use_property_split = True
1215 layout.use_property_decorate = False # No animation.
1217 sfile = context.space_data
1218 operator = sfile.active_operator
1220 layout.active = operator.export_animations
1222 layout.prop(operator, 'export_animation_mode')
1223 if operator.export_animation_mode == "ACTIVE_ACTIONS":
1224 layout.prop(operator, 'export_nla_strips_merged_animation_name')
1226 row = layout.row()
1227 row.active = operator.export_morph is True
1228 row.prop(operator, 'export_morph_animation')
1229 row = layout.row()
1230 row.active = operator.export_force_sampling and operator.export_animation_mode in ['ACTIONS', 'ACTIVE_ACTIONS']
1231 row.prop(operator, 'export_bake_animation')
1232 if operator.export_animation_mode == "SCENE":
1233 layout.prop(operator, 'export_anim_scene_split_object')
1235 class GLTF_PT_export_animation_notes(bpy.types.Panel):
1236 bl_space_type = 'FILE_BROWSER'
1237 bl_region_type = 'TOOL_PROPS'
1238 bl_label = "Notes"
1239 bl_parent_id = "GLTF_PT_export_animation"
1240 bl_options = {'DEFAULT_CLOSED'}
1242 @classmethod
1243 def poll(cls, context):
1244 sfile = context.space_data
1245 operator = sfile.active_operator
1247 return operator.bl_idname == "EXPORT_SCENE_OT_gltf" and \
1248 operator.export_animation_mode in ["NLA_TRACKS", "SCENE"]
1250 def draw(self, context):
1251 operator = context.space_data.active_operator
1252 layout = self.layout
1253 if operator.export_animation_mode == "SCENE":
1254 layout.label(text="Scene mode uses full bake mode:")
1255 layout.label(text="- sampling is active")
1256 layout.label(text="- baking all objects is active")
1257 layout.label(text="- Using scene frame range")
1258 elif operator.export_animation_mode == "NLA_TRACKS":
1259 layout.label(text="Track mode uses full bake mode:")
1260 layout.label(text="- sampling is active")
1261 layout.label(text="- baking all objects is active")
1263 class GLTF_PT_export_animation_ranges(bpy.types.Panel):
1264 bl_space_type = 'FILE_BROWSER'
1265 bl_region_type = 'TOOL_PROPS'
1266 bl_label = "Rest & Ranges"
1267 bl_parent_id = "GLTF_PT_export_animation"
1268 bl_options = {'DEFAULT_CLOSED'}
1270 @classmethod
1271 def poll(cls, context):
1272 sfile = context.space_data
1273 operator = sfile.active_operator
1275 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1277 def draw(self, context):
1278 layout = self.layout
1279 layout.use_property_split = True
1280 layout.use_property_decorate = False # No animation.
1282 sfile = context.space_data
1283 operator = sfile.active_operator
1285 layout.active = operator.export_animations
1287 layout.prop(operator, 'export_current_frame')
1288 row = layout.row()
1289 row.active = operator.export_animation_mode in ['ACTIONS', 'ACTIVE_ACTIONS', 'NLA_TRACKS']
1290 row.prop(operator, 'export_frame_range')
1291 layout.prop(operator, 'export_anim_slide_to_zero')
1292 row = layout.row()
1293 row.active = operator.export_animation_mode in ['ACTIONS', 'ACTIVE_ACTIONS', 'NLA_TRACKS']
1294 layout.prop(operator, 'export_negative_frame')
1296 class GLTF_PT_export_animation_armature(bpy.types.Panel):
1297 bl_space_type = 'FILE_BROWSER'
1298 bl_region_type = 'TOOL_PROPS'
1299 bl_label = "Armature"
1300 bl_parent_id = "GLTF_PT_export_animation"
1301 bl_options = {'DEFAULT_CLOSED'}
1303 @classmethod
1304 def poll(cls, context):
1305 sfile = context.space_data
1306 operator = sfile.active_operator
1308 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1310 def draw(self, context):
1311 layout = self.layout
1312 layout.use_property_split = True
1313 layout.use_property_decorate = False # No animation.
1315 sfile = context.space_data
1316 operator = sfile.active_operator
1318 layout.active = operator.export_animations
1320 layout.prop(operator, 'export_anim_single_armature')
1321 layout.prop(operator, 'export_reset_pose_bones')
1323 class GLTF_PT_export_animation_sampling(bpy.types.Panel):
1324 bl_space_type = 'FILE_BROWSER'
1325 bl_region_type = 'TOOL_PROPS'
1326 bl_label = "Sampling Animations"
1327 bl_parent_id = "GLTF_PT_export_animation"
1328 bl_options = {'DEFAULT_CLOSED'}
1330 @classmethod
1331 def poll(cls, context):
1332 sfile = context.space_data
1333 operator = sfile.active_operator
1335 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1337 def draw_header(self, context):
1338 sfile = context.space_data
1339 operator = sfile.active_operator
1340 self.layout.active = operator.export_animations and operator.export_animation_mode in ['ACTIONS', 'ACTIVE_ACTIONS']
1341 self.layout.prop(operator, "export_force_sampling", text="")
1343 def draw(self, context):
1344 layout = self.layout
1345 layout.use_property_split = True
1346 layout.use_property_decorate = False # No animation.
1348 sfile = context.space_data
1349 operator = sfile.active_operator
1351 layout.active = operator.export_animations
1353 layout.prop(operator, 'export_frame_step')
1356 class GLTF_PT_export_animation_optimize(bpy.types.Panel):
1357 bl_space_type = 'FILE_BROWSER'
1358 bl_region_type = 'TOOL_PROPS'
1359 bl_label = "Optimize Animations"
1360 bl_parent_id = "GLTF_PT_export_animation"
1361 bl_options = {'DEFAULT_CLOSED'}
1363 @classmethod
1364 def poll(cls, context):
1365 sfile = context.space_data
1366 operator = sfile.active_operator
1368 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1370 def draw(self, context):
1371 layout = self.layout
1372 layout.use_property_split = True
1373 layout.use_property_decorate = False # No animation.
1375 sfile = context.space_data
1376 operator = sfile.active_operator
1378 layout.active = operator.export_animations
1380 layout.prop(operator, 'export_optimize_animation_size')
1382 row = layout.row()
1383 row.prop(operator, 'export_optimize_animation_keep_anim_armature')
1385 row = layout.row()
1386 row.prop(operator, 'export_optimize_animation_keep_anim_object')
1389 class GLTF_PT_export_user_extensions(bpy.types.Panel):
1390 bl_space_type = 'FILE_BROWSER'
1391 bl_region_type = 'TOOL_PROPS'
1392 bl_label = "Exporter Extensions"
1393 bl_parent_id = "FILE_PT_operator"
1394 bl_options = {'DEFAULT_CLOSED'}
1396 @classmethod
1397 def poll(cls, context):
1398 sfile = context.space_data
1399 operator = sfile.active_operator
1401 return operator.bl_idname == "EXPORT_SCENE_OT_gltf" and operator.has_active_exporter_extensions
1403 def draw(self, context):
1404 layout = self.layout
1405 layout.use_property_split = True
1406 layout.use_property_decorate = False # No animation.
1408 class GLTF_PT_import_user_extensions(bpy.types.Panel):
1409 bl_space_type = 'FILE_BROWSER'
1410 bl_region_type = 'TOOL_PROPS'
1411 bl_label = "Importer Extensions"
1412 bl_parent_id = "FILE_PT_operator"
1413 bl_options = {'DEFAULT_CLOSED'}
1415 @classmethod
1416 def poll(cls, context):
1417 sfile = context.space_data
1418 operator = sfile.active_operator
1419 return operator.bl_idname == "IMPORT_SCENE_OT_gltf" and operator.has_active_importer_extensions
1421 def draw(self, context):
1422 layout = self.layout
1423 layout.use_property_split = True
1424 layout.use_property_decorate = False # No animation.
1426 class ExportGLTF2(bpy.types.Operator, ExportGLTF2_Base, ExportHelper):
1427 """Export scene as glTF 2.0 file"""
1428 bl_idname = 'export_scene.gltf'
1429 bl_label = 'Export glTF 2.0'
1431 filename_ext = ''
1433 filter_glob: StringProperty(default='*.glb', options={'HIDDEN'})
1436 def menu_func_export(self, context):
1437 self.layout.operator(ExportGLTF2.bl_idname, text='glTF 2.0 (.glb/.gltf)')
1440 class ImportGLTF2(Operator, ConvertGLTF2_Base, ImportHelper):
1441 """Load a glTF 2.0 file"""
1442 bl_idname = 'import_scene.gltf'
1443 bl_label = 'Import glTF 2.0'
1444 bl_options = {'REGISTER', 'UNDO'}
1446 filter_glob: StringProperty(default="*.glb;*.gltf", options={'HIDDEN'})
1448 files: CollectionProperty(
1449 name="File Path",
1450 type=bpy.types.OperatorFileListElement,
1453 loglevel: IntProperty(
1454 name='Log Level',
1455 description="Log Level")
1457 import_pack_images: BoolProperty(
1458 name='Pack Images',
1459 description='Pack all images into .blend file',
1460 default=True
1463 merge_vertices: BoolProperty(
1464 name='Merge Vertices',
1465 description=(
1466 'The glTF format requires discontinuous normals, UVs, and '
1467 'other vertex attributes to be stored as separate vertices, '
1468 'as required for rendering on typical graphics hardware. '
1469 'This option attempts to combine co-located vertices where possible. '
1470 'Currently cannot combine verts with different normals'
1472 default=False,
1475 import_shading: EnumProperty(
1476 name="Shading",
1477 items=(("NORMALS", "Use Normal Data", ""),
1478 ("FLAT", "Flat Shading", ""),
1479 ("SMOOTH", "Smooth Shading", "")),
1480 description="How normals are computed during import",
1481 default="NORMALS")
1483 bone_heuristic: EnumProperty(
1484 name="Bone Dir",
1485 items=(
1486 ("BLENDER", "Blender (best for re-importing)",
1487 "Good for re-importing glTFs exported from Blender. "
1488 "Bone tips are placed on their local +Y axis (in glTF space)"),
1489 ("TEMPERANCE", "Temperance (average)",
1490 "Decent all-around strategy. "
1491 "A bone with one child has its tip placed on the local axis "
1492 "closest to its child"),
1493 ("FORTUNE", "Fortune (may look better, less accurate)",
1494 "Might look better than Temperance, but also might have errors. "
1495 "A bone with one child has its tip placed at its child's root. "
1496 "Non-uniform scalings may get messed up though, so beware"),
1498 description="Heuristic for placing bones. Tries to make bones pretty",
1499 default="TEMPERANCE",
1502 guess_original_bind_pose: BoolProperty(
1503 name='Guess Original Bind Pose',
1504 description=(
1505 'Try to guess the original bind pose for skinned meshes from '
1506 'the inverse bind matrices. '
1507 'When off, use default/rest pose as bind pose'
1509 default=True,
1512 def draw(self, context):
1513 layout = self.layout
1515 layout.use_property_split = True
1516 layout.use_property_decorate = False # No animation.
1518 layout.prop(self, 'import_pack_images')
1519 layout.prop(self, 'merge_vertices')
1520 layout.prop(self, 'import_shading')
1521 layout.prop(self, 'guess_original_bind_pose')
1522 layout.prop(self, 'bone_heuristic')
1523 layout.prop(self, 'export_import_convert_lighting_mode')
1525 def invoke(self, context, event):
1526 import sys
1527 preferences = bpy.context.preferences
1528 for addon_name in preferences.addons.keys():
1529 try:
1530 if hasattr(sys.modules[addon_name], 'glTF2ImportUserExtension') or hasattr(sys.modules[addon_name], 'glTF2ImportUserExtensions'):
1531 importer_extension_panel_unregister_functors.append(sys.modules[addon_name].register_panel())
1532 except Exception:
1533 pass
1535 self.has_active_importer_extensions = len(importer_extension_panel_unregister_functors) > 0
1536 return ImportHelper.invoke(self, context, event)
1538 def execute(self, context):
1539 return self.import_gltf2(context)
1541 def import_gltf2(self, context):
1542 import os
1544 self.set_debug_log()
1545 import_settings = self.as_keywords()
1547 user_extensions = []
1549 import sys
1550 preferences = bpy.context.preferences
1551 for addon_name in preferences.addons.keys():
1552 try:
1553 module = sys.modules[addon_name]
1554 except Exception:
1555 continue
1556 if hasattr(module, 'glTF2ImportUserExtension'):
1557 extension_ctor = module.glTF2ImportUserExtension
1558 user_extensions.append(extension_ctor())
1559 import_settings['import_user_extensions'] = user_extensions
1561 if self.files:
1562 # Multiple file import
1563 ret = {'CANCELLED'}
1564 dirname = os.path.dirname(self.filepath)
1565 for file in self.files:
1566 path = os.path.join(dirname, file.name)
1567 if self.unit_import(path, import_settings) == {'FINISHED'}:
1568 ret = {'FINISHED'}
1569 return ret
1570 else:
1571 # Single file import
1572 return self.unit_import(self.filepath, import_settings)
1574 def unit_import(self, filename, import_settings):
1575 import time
1576 from .io.imp.gltf2_io_gltf import glTFImporter, ImportError
1577 from .blender.imp.gltf2_blender_gltf import BlenderGlTF
1579 try:
1580 gltf_importer = glTFImporter(filename, import_settings)
1581 gltf_importer.read()
1582 gltf_importer.checks()
1584 print("Data are loaded, start creating Blender stuff")
1586 start_time = time.time()
1587 BlenderGlTF.create(gltf_importer)
1588 elapsed_s = "{:.2f}s".format(time.time() - start_time)
1589 print("glTF import finished in " + elapsed_s)
1591 gltf_importer.log.removeHandler(gltf_importer.log_handler)
1593 return {'FINISHED'}
1595 except ImportError as e:
1596 self.report({'ERROR'}, e.args[0])
1597 return {'CANCELLED'}
1599 def set_debug_log(self):
1600 import logging
1601 if bpy.app.debug_value == 0:
1602 self.loglevel = logging.CRITICAL
1603 elif bpy.app.debug_value == 1:
1604 self.loglevel = logging.ERROR
1605 elif bpy.app.debug_value == 2:
1606 self.loglevel = logging.WARNING
1607 elif bpy.app.debug_value == 3:
1608 self.loglevel = logging.INFO
1609 else:
1610 self.loglevel = logging.NOTSET
1613 def gltf_variant_ui_update(self, context):
1614 from .blender.com.gltf2_blender_ui import variant_register, variant_unregister
1615 if self.KHR_materials_variants_ui is True:
1616 # register all needed types
1617 variant_register()
1618 else:
1619 variant_unregister()
1621 def gltf_animation_ui_update(self, context):
1622 from .blender.com.gltf2_blender_ui import anim_ui_register, anim_ui_unregister
1623 if self.animation_ui is True:
1624 # register all needed types
1625 anim_ui_register()
1626 else:
1627 anim_ui_unregister()
1629 class GLTF_AddonPreferences(bpy.types.AddonPreferences):
1630 bl_idname = __package__
1632 settings_node_ui : bpy.props.BoolProperty(
1633 default= False,
1634 description="Displays glTF Material Output node in Shader Editor (Menu Add > Output)"
1637 KHR_materials_variants_ui : bpy.props.BoolProperty(
1638 default= False,
1639 description="Displays glTF UI to manage material variants",
1640 update=gltf_variant_ui_update
1643 animation_ui: bpy.props.BoolProperty(
1644 default=False,
1645 description="Display glTF UI to manage animations",
1646 update=gltf_animation_ui_update
1649 def draw(self, context):
1650 layout = self.layout
1651 row = layout.row()
1652 row.prop(self, "settings_node_ui", text="Shader Editor Add-ons")
1653 row.prop(self, "KHR_materials_variants_ui", text="Material Variants")
1654 row.prop(self, "animation_ui", text="Animation UI")
1656 def menu_func_import(self, context):
1657 self.layout.operator(ImportGLTF2.bl_idname, text='glTF 2.0 (.glb/.gltf)')
1660 classes = (
1661 ExportGLTF2,
1662 GLTF_PT_export_main,
1663 GLTF_PT_export_include,
1664 GLTF_PT_export_transform,
1665 GLTF_PT_export_data,
1666 GLTF_PT_export_data_mesh,
1667 GLTF_PT_export_data_material,
1668 GLTF_PT_export_data_original_pbr,
1669 GLTF_PT_export_data_shapekeys,
1670 GLTF_PT_export_data_armature,
1671 GLTF_PT_export_data_skinning,
1672 GLTF_PT_export_data_lighting,
1673 GLTF_PT_export_data_compression,
1674 GLTF_PT_export_animation,
1675 GLTF_PT_export_animation_notes,
1676 GLTF_PT_export_animation_ranges,
1677 GLTF_PT_export_animation_armature,
1678 GLTF_PT_export_animation_sampling,
1679 GLTF_PT_export_animation_optimize,
1680 GLTF_PT_export_user_extensions,
1681 ImportGLTF2,
1682 GLTF_PT_import_user_extensions,
1683 GLTF_AddonPreferences
1687 def register():
1688 from .blender.com import gltf2_blender_ui as blender_ui
1689 for c in classes:
1690 bpy.utils.register_class(c)
1691 # bpy.utils.register_module(__name__)
1693 blender_ui.register()
1694 if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True:
1695 blender_ui.variant_register()
1696 if bpy.context.preferences.addons['io_scene_gltf2'].preferences.animation_ui is True:
1697 blender_ui.anim_ui_register()
1699 # add to the export / import menu
1700 bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
1701 bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
1704 def unregister():
1705 from .blender.com import gltf2_blender_ui as blender_ui
1706 blender_ui.unregister()
1707 if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True:
1708 blender_ui.variant_unregister()
1710 for c in classes:
1711 bpy.utils.unregister_class(c)
1712 for f in exporter_extension_panel_unregister_functors:
1714 exporter_extension_panel_unregister_functors.clear()
1716 for f in importer_extension_panel_unregister_functors:
1718 importer_extension_panel_unregister_functors.clear()
1720 # bpy.utils.unregister_module(__name__)
1722 # remove from the export / import menu
1723 bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
1724 bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)