Merge branch 'blender-v4.0-release'
[blender-addons.git] / io_scene_gltf2 / __init__.py
blobf3085ffd5cb93bb71932a180603351cef2a3dc0e
1 # SPDX-FileCopyrightText: 2018-2021 The glTF-Blender-IO authors
3 # SPDX-License-Identifier: Apache-2.0
5 bl_info = {
6 'name': 'glTF 2.0 format',
7 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
8 "version": (4, 1, 2),
9 'blender': (4, 0, 0),
10 'location': 'File > Import-Export',
11 'description': 'Import-Export as glTF 2.0',
12 'warning': '',
13 'doc_url': "{BLENDER_MANUAL_URL}/addons/import_export/scene_gltf2.html",
14 'tracker_url': "https://github.com/KhronosGroup/glTF-Blender-IO/issues/",
15 'support': 'OFFICIAL',
16 'category': 'Import-Export',
19 def get_version_string():
20 return str(bl_info['version'][0]) + '.' + str(bl_info['version'][1]) + '.' + str(bl_info['version'][2])
23 # Script reloading (if the user calls 'Reload Scripts' from Blender)
26 def reload_package(module_dict_main):
27 import importlib
28 from pathlib import Path
30 def reload_package_recursive(current_dir, module_dict):
31 for path in current_dir.iterdir():
32 if "__init__" in str(path) or path.stem not in module_dict:
33 continue
35 if path.is_file() and path.suffix == ".py":
36 importlib.reload(module_dict[path.stem])
37 elif path.is_dir():
38 reload_package_recursive(path, module_dict[path.stem].__dict__)
40 reload_package_recursive(Path(__file__).parent, module_dict_main)
43 if "bpy" in locals():
44 reload_package(locals())
46 import bpy
47 from bpy.props import (StringProperty,
48 BoolProperty,
49 EnumProperty,
50 IntProperty,
51 CollectionProperty)
52 from bpy.types import Operator
53 from bpy_extras.io_utils import ImportHelper, ExportHelper
57 # Functions / Classes.
60 exporter_extension_panel_unregister_functors = []
61 importer_extension_panel_unregister_functors = []
64 def ensure_filepath_matches_export_format(filepath, export_format):
65 import os
66 filename = os.path.basename(filepath)
67 if not filename:
68 return filepath
70 stem, ext = os.path.splitext(filename)
71 if stem.startswith('.') and not ext:
72 stem, ext = '', stem
74 desired_ext = '.glb' if export_format == 'GLB' else '.gltf'
75 ext_lower = ext.lower()
76 if ext_lower not in ['.glb', '.gltf']:
77 return filepath + desired_ext
78 elif ext_lower != desired_ext:
79 filepath = filepath[:-len(ext)] # strip off ext
80 return filepath + desired_ext
81 else:
82 return filepath
85 def on_export_format_changed(self, context):
86 # Update the filename in the file browser when the format (.glb/.gltf)
87 # changes
88 sfile = context.space_data
89 if not isinstance(sfile, bpy.types.SpaceFileBrowser):
90 return
91 if not sfile.active_operator:
92 return
93 if sfile.active_operator.bl_idname != "EXPORT_SCENE_OT_gltf":
94 return
96 sfile.params.filename = ensure_filepath_matches_export_format(
97 sfile.params.filename,
98 self.export_format,
101 # Also change the filter
102 sfile.params.filter_glob = '*.glb' if self.export_format == 'GLB' else '*.gltf'
103 # Force update of file list, because update the filter does not update the real file list
104 bpy.ops.file.refresh()
107 class ConvertGLTF2_Base:
108 """Base class containing options that should be exposed during both import and export."""
110 export_import_convert_lighting_mode: EnumProperty(
111 name='Lighting Mode',
112 items=(
113 ('SPEC', 'Standard', 'Physically-based glTF lighting units (cd, lx, nt)'),
114 ('COMPAT', 'Unitless', 'Non-physical, unitless lighting. Useful when exposure controls are not available'),
115 ('RAW', 'Raw (Deprecated)', 'Blender lighting strengths with no conversion'),
117 description='Optional backwards compatibility for non-standard render engines. Applies to lights',# TODO: and emissive materials',
118 default='SPEC'
121 class ExportGLTF2_Base(ConvertGLTF2_Base):
122 # TODO: refactor to avoid boilerplate
124 def __init__(self):
125 from .io.com import gltf2_io_draco_compression_extension
126 self.is_draco_available = gltf2_io_draco_compression_extension.dll_exists()
128 bl_options = {'PRESET'}
130 # Don't use export_ prefix here, I don't want it to be saved with other export settings
131 gltf_export_id: StringProperty(
132 name='Identifier',
133 description=(
134 'Identifier of caller (in case of add-on calling this exporter). '
135 'Can be useful in case of Extension added by other add-ons'
137 default=''
140 export_format: EnumProperty(
141 name='Format',
142 items=(('GLB', 'glTF Binary (.glb)',
143 'Exports a single file, with all data packed in binary form. '
144 'Most efficient and portable, but more difficult to edit later'),
145 ('GLTF_SEPARATE', 'glTF Separate (.gltf + .bin + textures)',
146 'Exports multiple files, with separate JSON, binary and texture data. '
147 'Easiest to edit later'),
148 ('GLTF_EMBEDDED', 'glTF Embedded (.gltf)',
149 'Exports a single file, with all data packed in JSON. '
150 'Less efficient than binary, but easier to edit later')),
151 description=(
152 'Output format and embedding options. Binary is most efficient, '
153 'but JSON (embedded or separate) may be easier to edit later'
155 default='GLB', #Warning => If you change the default, need to change the default filter too
156 update=on_export_format_changed,
159 ui_tab: EnumProperty(
160 items=(('GENERAL', "General", "General settings"),
161 ('MESHES', "Meshes", "Mesh settings"),
162 ('OBJECTS', "Objects", "Object settings"),
163 ('ANIMATION', "Animation", "Animation settings")),
164 name="ui_tab",
165 description="Export setting categories",
168 export_copyright: StringProperty(
169 name='Copyright',
170 description='Legal rights and conditions for the model',
171 default=''
174 export_image_format: EnumProperty(
175 name='Images',
176 items=(('AUTO', 'Automatic',
177 'Save PNGs as PNGs, JPEGs as JPEGs, WEBPs as WEBPs. '
178 'If neither one, use PNG'),
179 ('JPEG', 'JPEG Format (.jpg)',
180 'Save images as JPEGs. (Images that need alpha are saved as PNGs though.) '
181 'Be aware of a possible loss in quality'),
182 ('WEBP', 'Webp Format',
183 'Save images as WEBPs as main image (no fallback)'),
184 ('NONE', 'None',
185 'Don\'t export images'),
187 description=(
188 'Output format for images. PNG is lossless and generally preferred, but JPEG might be preferable for web '
189 'applications due to the smaller file size. Alternatively they can be omitted if they are not needed'
191 default='AUTO'
194 export_image_add_webp: BoolProperty(
195 name='Create Webp',
196 description=(
197 "Creates webp textures for every textures. "
198 "For already webp textures, nothing happen"
200 default=False
203 export_image_webp_fallback: BoolProperty(
204 name='Webp fallback',
205 description=(
206 "For all webp textures, create a PNG fallback texture"
208 default=False
211 export_texture_dir: StringProperty(
212 name='Textures',
213 description='Folder to place texture files in. Relative to the .gltf file',
214 default='',
217 # Keep for back compatibility
218 export_jpeg_quality: IntProperty(
219 name='JPEG quality',
220 description='Quality of JPEG export',
221 default=75,
222 min=0,
223 max=100
226 # Keep for back compatibility
227 export_image_quality: IntProperty(
228 name='Image quality',
229 description='Quality of image export',
230 default=75,
231 min=0,
232 max=100
235 export_keep_originals: BoolProperty(
236 name='Keep original',
237 description=('Keep original textures files if possible. '
238 'WARNING: if you use more than one texture, '
239 'where pbr standard requires only one, only one texture will be used. '
240 'This can lead to unexpected results'
242 default=False,
245 export_texcoords: BoolProperty(
246 name='UVs',
247 description='Export UVs (texture coordinates) with meshes',
248 default=True
251 export_normals: BoolProperty(
252 name='Normals',
253 description='Export vertex normals with meshes',
254 default=True
257 export_draco_mesh_compression_enable: BoolProperty(
258 name='Draco mesh compression',
259 description='Compress mesh using Draco',
260 default=False
263 export_draco_mesh_compression_level: IntProperty(
264 name='Compression level',
265 description='Compression level (0 = most speed, 6 = most compression, higher values currently not supported)',
266 default=6,
267 min=0,
268 max=10
271 export_draco_position_quantization: IntProperty(
272 name='Position quantization bits',
273 description='Quantization bits for position values (0 = no quantization)',
274 default=14,
275 min=0,
276 max=30
279 export_draco_normal_quantization: IntProperty(
280 name='Normal quantization bits',
281 description='Quantization bits for normal values (0 = no quantization)',
282 default=10,
283 min=0,
284 max=30
287 export_draco_texcoord_quantization: IntProperty(
288 name='Texcoord quantization bits',
289 description='Quantization bits for texture coordinate values (0 = no quantization)',
290 default=12,
291 min=0,
292 max=30
295 export_draco_color_quantization: IntProperty(
296 name='Color quantization bits',
297 description='Quantization bits for color values (0 = no quantization)',
298 default=10,
299 min=0,
300 max=30
303 export_draco_generic_quantization: IntProperty(
304 name='Generic quantization bits',
305 description='Quantization bits for generic values like weights or joints (0 = no quantization)',
306 default=12,
307 min=0,
308 max=30
311 export_tangents: BoolProperty(
312 name='Tangents',
313 description='Export vertex tangents with meshes',
314 default=False
317 export_materials: EnumProperty(
318 name='Materials',
319 items=(('EXPORT', 'Export',
320 'Export all materials used by included objects'),
321 ('PLACEHOLDER', 'Placeholder',
322 'Do not export materials, but write multiple primitive groups per mesh, keeping material slot information'),
323 ('NONE', 'No export',
324 'Do not export materials, and combine mesh primitive groups, losing material slot information')),
325 description='Export materials',
326 default='EXPORT'
329 export_colors: BoolProperty(
330 name='Vertex Colors',
331 description='Export vertex colors with meshes',
332 default=True
335 export_attributes: BoolProperty(
336 name='Attributes',
337 description='Export Attributes (when starting with underscore)',
338 default=False
341 use_mesh_edges: BoolProperty(
342 name='Loose Edges',
343 description=(
344 'Export loose edges as lines, using the material from the first material slot'
346 default=False,
349 use_mesh_vertices: BoolProperty(
350 name='Loose Points',
351 description=(
352 'Export loose points as glTF points, using the material from the first material slot'
354 default=False,
357 export_cameras: BoolProperty(
358 name='Cameras',
359 description='Export cameras',
360 default=False
363 use_selection: BoolProperty(
364 name='Selected Objects',
365 description='Export selected objects only',
366 default=False
369 use_visible: BoolProperty(
370 name='Visible Objects',
371 description='Export visible objects only',
372 default=False
375 use_renderable: BoolProperty(
376 name='Renderable Objects',
377 description='Export renderable objects only',
378 default=False
381 use_active_collection_with_nested: BoolProperty(
382 name='Include Nested Collections',
383 description='Include active collection and nested collections',
384 default=True
387 use_active_collection: BoolProperty(
388 name='Active Collection',
389 description='Export objects in the active collection only',
390 default=False
393 use_active_scene: BoolProperty(
394 name='Active Scene',
395 description='Export active scene only',
396 default=False
399 export_extras: BoolProperty(
400 name='Custom Properties',
401 description='Export custom properties as glTF extras',
402 default=False
405 export_yup: BoolProperty(
406 name='+Y Up',
407 description='Export using glTF convention, +Y up',
408 default=True
411 export_apply: BoolProperty(
412 name='Apply Modifiers',
413 description='Apply modifiers (excluding Armatures) to mesh objects -'
414 'WARNING: prevents exporting shape keys',
415 default=False
418 export_animations: BoolProperty(
419 name='Animations',
420 description='Exports active actions and NLA tracks as glTF animations',
421 default=True
424 export_frame_range: BoolProperty(
425 name='Limit to Playback Range',
426 description='Clips animations to selected playback range',
427 default=False
430 export_frame_step: IntProperty(
431 name='Sampling Rate',
432 description='How often to evaluate animated values (in frames)',
433 default=1,
434 min=1,
435 max=120
438 export_force_sampling: BoolProperty(
439 name='Always Sample Animations',
440 description='Apply sampling to all animations',
441 default=True
444 export_animation_mode: EnumProperty(
445 name='Animation mode',
446 items=(('ACTIONS', 'Actions',
447 'Export actions (actives and on NLA tracks) as separate animations'),
448 ('ACTIVE_ACTIONS', 'Active actions merged',
449 'All the currently assigned actions become one glTF animation'),
450 ('NLA_TRACKS', 'NLA Tracks',
451 'Export individual NLA Tracks as separate animation'),
452 ('SCENE', 'Scene',
453 'Export baked scene as a single animation')
455 description='Export Animation mode',
456 default='ACTIONS'
459 export_nla_strips_merged_animation_name: StringProperty(
460 name='Merged Animation Name',
461 description=(
462 "Name of single glTF animation to be exported"
464 default='Animation'
467 export_def_bones: BoolProperty(
468 name='Export Deformation Bones Only',
469 description='Export Deformation bones only',
470 default=False
473 export_hierarchy_flatten_bones: BoolProperty(
474 name='Flatten Bone Hierarchy',
475 description='Flatten Bone Hierarchy. Useful in case of non decomposable transformation matrix',
476 default=False
479 export_optimize_animation_size: BoolProperty(
480 name='Optimize Animation Size',
481 description=(
482 "Reduce exported file size by removing duplicate keyframes"
484 default=True
487 export_optimize_animation_keep_anim_armature: BoolProperty(
488 name='Force keeping channels for bones',
489 description=(
490 "If all keyframes are identical in a rig, "
491 "force keeping the minimal animation. "
492 "When off, all possible channels for "
493 "the bones will be exported, even if empty "
494 "(minimal animation, 2 keyframes)"
496 default=True
499 export_optimize_animation_keep_anim_object: BoolProperty(
500 name='Force keeping channel for objects',
501 description=(
502 "If all keyframes are identical for object transformations, "
503 "force keeping the minimal animation"
505 default=False
508 export_negative_frame: EnumProperty(
509 name='Negative Frames',
510 items=(('SLIDE', 'Slide',
511 'Slide animation to start at frame 0'),
512 ('CROP', 'Crop',
513 'Keep only frames above frame 0'),
515 description='Negative Frames are slid or cropped',
516 default='SLIDE'
519 export_anim_slide_to_zero: BoolProperty(
520 name='Set all glTF Animation starting at 0',
521 description=(
522 "Set all glTF animation starting at 0.0s. "
523 "Can be useful for looping animations"
525 default=False
528 export_bake_animation: BoolProperty(
529 name='Bake All Objects Animations',
530 description=(
531 "Force exporting animation on every object. "
532 "Can be useful when using constraints or driver. "
533 "Also useful when exporting only selection"
535 default=False
538 export_anim_single_armature: BoolProperty(
539 name='Export all Armature Actions',
540 description=(
541 "Export all actions, bound to a single armature. "
542 "WARNING: Option does not support exports including multiple armatures"
544 default=True
547 export_reset_pose_bones: BoolProperty(
548 name='Reset pose bones between actions',
549 description=(
550 "Reset pose bones between each action exported. "
551 "This is needed when some bones are not keyed on some animations"
553 default=True
556 export_current_frame: BoolProperty(
557 name='Use Current Frame as Object Rest Transformations',
558 description=(
559 'Export the scene in the current animation frame. '
560 'When off, frame 0 is used as rest transformations for objects'
562 default=False
565 export_rest_position_armature: BoolProperty(
566 name='Use Rest Position Armature',
567 description=(
568 "Export armatures using rest position as joints' rest pose. "
569 "When off, current frame pose is used as rest pose"
571 default=True
574 export_anim_scene_split_object: BoolProperty(
575 name='Split Animation by Object',
576 description=(
577 "Export Scene as seen in Viewport, "
578 "But split animation by Object"
580 default=True
583 export_skins: BoolProperty(
584 name='Skinning',
585 description='Export skinning (armature) data',
586 default=True
589 export_influence_nb: IntProperty(
590 name='Bone Influences',
591 description='Choose how many Bone influences to export',
592 default=4,
593 min=1
596 export_all_influences: BoolProperty(
597 name='Include All Bone Influences',
598 description='Allow export of all joint vertex influences. Models may appear incorrectly in many viewers',
599 default=False
602 export_morph: BoolProperty(
603 name='Shape Keys',
604 description='Export shape keys (morph targets)',
605 default=True
608 export_morph_normal: BoolProperty(
609 name='Shape Key Normals',
610 description='Export vertex normals with shape keys (morph targets)',
611 default=True
614 export_morph_tangent: BoolProperty(
615 name='Shape Key Tangents',
616 description='Export vertex tangents with shape keys (morph targets)',
617 default=False
620 export_morph_animation: BoolProperty(
621 name='Shape Key Animations',
622 description='Export shape keys animations (morph targets)',
623 default=True
626 export_morph_reset_sk_data: BoolProperty(
627 name='Reset shape keys between actions',
628 description=(
629 "Reset shape keys between each action exported. "
630 "This is needed when some SK channels are not keyed on some animations"
632 default=True
635 export_lights: BoolProperty(
636 name='Punctual Lights',
637 description='Export directional, point, and spot lights. '
638 'Uses "KHR_lights_punctual" glTF extension',
639 default=False
642 export_try_sparse_sk: BoolProperty(
643 name='Use Sparse Accessor if better',
644 description='Try using Sparce Accessor if it save space',
645 default=True
648 export_try_omit_sparse_sk: BoolProperty(
649 name='Omitting Sparse Accessor if data is empty',
650 description='Omitting Sparse Accessor if data is empty',
651 default=False
654 export_gpu_instances: BoolProperty(
655 name='GPU Instances',
656 description='Export using EXT_mesh_gpu_instancing.'
657 'Limited to children of a same Empty. '
658 'multiple Materials might be omitted',
659 default=False
662 # This parameter is only here for backward compatibility, as this option is removed in 3.6
663 # This option does nothing, and is not displayed in UI
664 # What you are looking for is probably "export_animation_mode"
665 export_nla_strips: BoolProperty(
666 name='Group by NLA Track',
667 description=(
668 "When on, multiple actions become part of the same glTF animation if "
669 "they're pushed onto NLA tracks with the same name. "
670 "When off, all the currently assigned actions become one glTF animation"
672 default=True
675 # Keep for back compatibility, but no more used
676 export_original_specular: BoolProperty(
677 name='Export original PBR Specular',
678 description=(
679 'Export original glTF PBR Specular, instead of Blender Principled Shader Specular'
681 default=False,
684 will_save_settings: BoolProperty(
685 name='Remember Export Settings',
686 description='Store glTF export settings in the Blender project',
687 default=False)
689 # Custom scene property for saving settings
690 scene_key = "glTF2ExportSettings"
694 def check(self, _context):
695 # Ensure file extension matches format
696 old_filepath = self.filepath
697 self.filepath = ensure_filepath_matches_export_format(
698 self.filepath,
699 self.export_format,
701 return self.filepath != old_filepath
703 def invoke(self, context, event):
704 settings = context.scene.get(self.scene_key)
705 self.will_save_settings = False
706 if settings:
707 try:
708 for (k, v) in settings.items():
709 setattr(self, k, v)
710 self.will_save_settings = True
712 # Update filter if user saved settings
713 if hasattr(self, 'export_format'):
714 self.filter_glob = '*.glb' if self.export_format == 'GLB' else '*.gltf'
716 except (AttributeError, TypeError):
717 self.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings")
718 del context.scene[self.scene_key]
720 import sys
721 preferences = bpy.context.preferences
722 for addon_name in preferences.addons.keys():
723 try:
724 if hasattr(sys.modules[addon_name], 'glTF2ExportUserExtension') or hasattr(sys.modules[addon_name], 'glTF2ExportUserExtensions'):
725 exporter_extension_panel_unregister_functors.append(sys.modules[addon_name].register_panel())
726 except Exception:
727 pass
729 self.has_active_exporter_extensions = len(exporter_extension_panel_unregister_functors) > 0
730 return ExportHelper.invoke(self, context, event)
732 def save_settings(self, context):
733 # find all props to save
734 exceptional = [
735 # options that don't start with 'export_'
736 'use_selection',
737 'use_visible',
738 'use_renderable',
739 'use_active_collection_with_nested',
740 'use_active_collection',
741 'use_mesh_edges',
742 'use_mesh_vertices',
743 'use_active_scene',
745 all_props = self.properties
746 export_props = {
747 x: getattr(self, x) for x in dir(all_props)
748 if (x.startswith("export_") or x in exceptional) and all_props.get(x) is not None
750 context.scene[self.scene_key] = export_props
752 def execute(self, context):
753 import os
754 import datetime
755 from .blender.exp import gltf2_blender_export
756 from .io.com.gltf2_io_path import path_to_uri
758 if self.will_save_settings:
759 self.save_settings(context)
761 self.check(context) # ensure filepath has the right extension
763 # All custom export settings are stored in this container.
764 export_settings = {}
766 export_settings['timestamp'] = datetime.datetime.now()
767 export_settings['gltf_export_id'] = self.gltf_export_id
768 export_settings['gltf_filepath'] = self.filepath
769 export_settings['gltf_filedirectory'] = os.path.dirname(export_settings['gltf_filepath']) + '/'
770 export_settings['gltf_texturedirectory'] = os.path.join(
771 export_settings['gltf_filedirectory'],
772 self.export_texture_dir,
774 export_settings['gltf_keep_original_textures'] = self.export_keep_originals
776 export_settings['gltf_format'] = self.export_format
777 export_settings['gltf_image_format'] = self.export_image_format
778 export_settings['gltf_add_webp'] = self.export_image_add_webp
779 export_settings['gltf_webp_fallback'] = self.export_image_webp_fallback
780 export_settings['gltf_image_quality'] = self.export_image_quality
781 export_settings['gltf_image_quality'] = self.export_jpeg_quality #For back compatibility
782 export_settings['gltf_copyright'] = self.export_copyright
783 export_settings['gltf_texcoords'] = self.export_texcoords
784 export_settings['gltf_normals'] = self.export_normals
785 export_settings['gltf_tangents'] = self.export_tangents and self.export_normals
786 export_settings['gltf_loose_edges'] = self.use_mesh_edges
787 export_settings['gltf_loose_points'] = self.use_mesh_vertices
789 if self.is_draco_available:
790 export_settings['gltf_draco_mesh_compression'] = self.export_draco_mesh_compression_enable
791 export_settings['gltf_draco_mesh_compression_level'] = self.export_draco_mesh_compression_level
792 export_settings['gltf_draco_position_quantization'] = self.export_draco_position_quantization
793 export_settings['gltf_draco_normal_quantization'] = self.export_draco_normal_quantization
794 export_settings['gltf_draco_texcoord_quantization'] = self.export_draco_texcoord_quantization
795 export_settings['gltf_draco_color_quantization'] = self.export_draco_color_quantization
796 export_settings['gltf_draco_generic_quantization'] = self.export_draco_generic_quantization
797 else:
798 export_settings['gltf_draco_mesh_compression'] = False
800 export_settings['gltf_materials'] = self.export_materials
801 export_settings['gltf_colors'] = self.export_colors
802 export_settings['gltf_attributes'] = self.export_attributes
803 export_settings['gltf_cameras'] = self.export_cameras
805 export_settings['gltf_visible'] = self.use_visible
806 export_settings['gltf_renderable'] = self.use_renderable
808 export_settings['gltf_active_collection'] = self.use_active_collection
809 if self.use_active_collection:
810 export_settings['gltf_active_collection_with_nested'] = self.use_active_collection_with_nested
811 else:
812 export_settings['gltf_active_collection_with_nested'] = False
813 export_settings['gltf_active_scene'] = self.use_active_scene
815 export_settings['gltf_selected'] = self.use_selection
816 export_settings['gltf_layers'] = True # self.export_layers
817 export_settings['gltf_extras'] = self.export_extras
818 export_settings['gltf_yup'] = self.export_yup
819 export_settings['gltf_apply'] = self.export_apply
820 export_settings['gltf_current_frame'] = self.export_current_frame
821 export_settings['gltf_animations'] = self.export_animations
822 export_settings['gltf_def_bones'] = self.export_def_bones
823 export_settings['gltf_flatten_bones_hierarchy'] = self.export_hierarchy_flatten_bones
824 if self.export_animations:
825 export_settings['gltf_frame_range'] = self.export_frame_range
826 export_settings['gltf_force_sampling'] = self.export_force_sampling
827 if not self.export_force_sampling:
828 export_settings['gltf_def_bones'] = False
829 export_settings['gltf_bake_animation'] = False
830 export_settings['gltf_animation_mode'] = self.export_animation_mode
831 if export_settings['gltf_animation_mode'] == "NLA_TRACKS":
832 export_settings['gltf_force_sampling'] = True
833 if export_settings['gltf_animation_mode'] == "SCENE":
834 export_settings['gltf_anim_scene_split_object'] = self.export_anim_scene_split_object
835 else:
836 export_settings['gltf_anim_scene_split_object'] = False
838 export_settings['gltf_nla_strips_merged_animation_name'] = self.export_nla_strips_merged_animation_name
839 export_settings['gltf_optimize_animation'] = self.export_optimize_animation_size
840 export_settings['gltf_optimize_animation_keep_armature'] = self.export_optimize_animation_keep_anim_armature
841 export_settings['gltf_optimize_animation_keep_object'] = self.export_optimize_animation_keep_anim_object
842 export_settings['gltf_export_anim_single_armature'] = self.export_anim_single_armature
843 export_settings['gltf_export_reset_pose_bones'] = self.export_reset_pose_bones
844 export_settings['gltf_export_reset_sk_data'] = self.export_morph_reset_sk_data
845 export_settings['gltf_bake_animation'] = self.export_bake_animation
846 export_settings['gltf_negative_frames'] = self.export_negative_frame
847 export_settings['gltf_anim_slide_to_zero'] = self.export_anim_slide_to_zero
848 else:
849 export_settings['gltf_frame_range'] = False
850 export_settings['gltf_force_sampling'] = False
851 export_settings['gltf_bake_animation'] = False
852 export_settings['gltf_optimize_animation'] = False
853 export_settings['gltf_optimize_animation_keep_armature'] = False
854 export_settings['gltf_optimize_animation_keep_object'] = False
855 export_settings['gltf_export_anim_single_armature'] = False
856 export_settings['gltf_export_reset_pose_bones'] = False
857 export_settings['gltf_export_reset_sk_data'] = False
858 export_settings['gltf_skins'] = self.export_skins
859 if self.export_skins:
860 export_settings['gltf_all_vertex_influences'] = self.export_all_influences
861 export_settings['gltf_vertex_influences_nb'] = self.export_influence_nb
862 else:
863 export_settings['gltf_all_vertex_influences'] = False
864 export_settings['gltf_def_bones'] = False
865 export_settings['gltf_rest_position_armature'] = self.export_rest_position_armature
866 export_settings['gltf_frame_step'] = self.export_frame_step
868 export_settings['gltf_morph'] = self.export_morph
869 if self.export_morph:
870 export_settings['gltf_morph_normal'] = self.export_morph_normal
871 export_settings['gltf_morph_tangent'] = self.export_morph_tangent
872 export_settings['gltf_morph_anim'] = self.export_morph_animation
873 else:
874 export_settings['gltf_morph_normal'] = False
875 export_settings['gltf_morph_tangent'] = False
876 export_settings['gltf_morph_anim'] = False
878 export_settings['gltf_lights'] = self.export_lights
879 export_settings['gltf_lighting_mode'] = self.export_import_convert_lighting_mode
880 export_settings['gltf_gpu_instances'] = self.export_gpu_instances
882 export_settings['gltf_try_sparse_sk'] = self.export_try_sparse_sk
883 export_settings['gltf_try_omit_sparse_sk'] = self.export_try_omit_sparse_sk
884 if not self.export_try_sparse_sk:
885 export_settings['gltf_try_omit_sparse_sk'] = False
888 export_settings['gltf_binary'] = bytearray()
889 export_settings['gltf_binaryfilename'] = (
890 path_to_uri(os.path.splitext(os.path.basename(self.filepath))[0] + '.bin')
893 user_extensions = []
894 pre_export_callbacks = []
895 post_export_callbacks = []
897 import sys
898 preferences = bpy.context.preferences
899 for addon_name in preferences.addons.keys():
900 try:
901 module = sys.modules[addon_name]
902 except Exception:
903 continue
904 if hasattr(module, 'glTF2ExportUserExtension'):
905 extension_ctor = module.glTF2ExportUserExtension
906 user_extensions.append(extension_ctor())
907 if hasattr(module, 'glTF2ExportUserExtensions'):
908 extension_ctors = module.glTF2ExportUserExtensions
909 for extension_ctor in extension_ctors:
910 user_extensions.append(extension_ctor())
911 if hasattr(module, 'glTF2_pre_export_callback'):
912 pre_export_callbacks.append(module.glTF2_pre_export_callback)
913 if hasattr(module, 'glTF2_post_export_callback'):
914 post_export_callbacks.append(module.glTF2_post_export_callback)
915 export_settings['gltf_user_extensions'] = user_extensions
916 export_settings['pre_export_callbacks'] = pre_export_callbacks
917 export_settings['post_export_callbacks'] = post_export_callbacks
919 return gltf2_blender_export.save(context, export_settings)
921 def draw(self, context):
922 pass # Is needed to get panels available
925 class GLTF_PT_export_main(bpy.types.Panel):
926 bl_space_type = 'FILE_BROWSER'
927 bl_region_type = 'TOOL_PROPS'
928 bl_label = ""
929 bl_parent_id = "FILE_PT_operator"
930 bl_options = {'HIDE_HEADER'}
932 @classmethod
933 def poll(cls, context):
934 sfile = context.space_data
935 operator = sfile.active_operator
937 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
939 def draw(self, context):
940 layout = self.layout
941 layout.use_property_split = True
942 layout.use_property_decorate = False # No animation.
944 sfile = context.space_data
945 operator = sfile.active_operator
947 layout.prop(operator, 'export_format')
948 if operator.export_format == 'GLTF_SEPARATE':
949 layout.prop(operator, 'export_keep_originals')
950 if operator.export_keep_originals is False:
951 layout.prop(operator, 'export_texture_dir', icon='FILE_FOLDER')
953 layout.prop(operator, 'export_copyright')
954 layout.prop(operator, 'will_save_settings')
957 class GLTF_PT_export_include(bpy.types.Panel):
958 bl_space_type = 'FILE_BROWSER'
959 bl_region_type = 'TOOL_PROPS'
960 bl_label = "Include"
961 bl_parent_id = "FILE_PT_operator"
962 bl_options = {'DEFAULT_CLOSED'}
964 @classmethod
965 def poll(cls, context):
966 sfile = context.space_data
967 operator = sfile.active_operator
969 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
971 def draw(self, context):
972 layout = self.layout
973 layout.use_property_split = True
974 layout.use_property_decorate = False # No animation.
976 sfile = context.space_data
977 operator = sfile.active_operator
979 col = layout.column(heading = "Limit to", align = True)
980 col.prop(operator, 'use_selection')
981 col.prop(operator, 'use_visible')
982 col.prop(operator, 'use_renderable')
983 col.prop(operator, 'use_active_collection')
984 if operator.use_active_collection:
985 col.prop(operator, 'use_active_collection_with_nested')
986 col.prop(operator, 'use_active_scene')
988 col = layout.column(heading = "Data", align = True)
989 col.prop(operator, 'export_extras')
990 col.prop(operator, 'export_cameras')
991 col.prop(operator, 'export_lights')
994 class GLTF_PT_export_transform(bpy.types.Panel):
995 bl_space_type = 'FILE_BROWSER'
996 bl_region_type = 'TOOL_PROPS'
997 bl_label = "Transform"
998 bl_parent_id = "FILE_PT_operator"
999 bl_options = {'DEFAULT_CLOSED'}
1001 @classmethod
1002 def poll(cls, context):
1003 sfile = context.space_data
1004 operator = sfile.active_operator
1006 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1008 def draw(self, context):
1009 layout = self.layout
1010 layout.use_property_split = True
1011 layout.use_property_decorate = False # No animation.
1013 sfile = context.space_data
1014 operator = sfile.active_operator
1016 layout.prop(operator, 'export_yup')
1019 class GLTF_PT_export_data(bpy.types.Panel):
1020 bl_space_type = 'FILE_BROWSER'
1021 bl_region_type = 'TOOL_PROPS'
1022 bl_label = "Data"
1023 bl_parent_id = "FILE_PT_operator"
1024 bl_options = {'DEFAULT_CLOSED'}
1026 @classmethod
1027 def poll(cls, context):
1028 sfile = context.space_data
1029 operator = sfile.active_operator
1031 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1033 def draw(self, context):
1034 pass
1036 class GLTF_PT_export_data_scene(bpy.types.Panel):
1037 bl_space_type = 'FILE_BROWSER'
1038 bl_region_type = 'TOOL_PROPS'
1039 bl_label = "Scene Graph"
1040 bl_parent_id = "GLTF_PT_export_data"
1041 bl_options = {'DEFAULT_CLOSED'}
1043 @classmethod
1044 def poll(cls, context):
1045 sfile = context.space_data
1046 operator = sfile.active_operator
1047 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1049 def draw(self, context):
1050 layout = self.layout
1051 layout.use_property_split = True
1052 layout.use_property_decorate = False # No animation.
1054 sfile = context.space_data
1055 operator = sfile.active_operator
1056 layout.prop(operator, 'export_gpu_instances')
1058 class GLTF_PT_export_data_mesh(bpy.types.Panel):
1059 bl_space_type = 'FILE_BROWSER'
1060 bl_region_type = 'TOOL_PROPS'
1061 bl_label = "Mesh"
1062 bl_parent_id = "GLTF_PT_export_data"
1063 bl_options = {'DEFAULT_CLOSED'}
1065 @classmethod
1066 def poll(cls, context):
1067 sfile = context.space_data
1068 operator = sfile.active_operator
1069 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1071 def draw(self, context):
1072 layout = self.layout
1073 layout.use_property_split = True
1074 layout.use_property_decorate = False # No animation.
1076 sfile = context.space_data
1077 operator = sfile.active_operator
1079 layout.prop(operator, 'export_apply')
1080 layout.prop(operator, 'export_texcoords')
1081 layout.prop(operator, 'export_normals')
1082 col = layout.column()
1083 col.active = operator.export_normals
1084 col.prop(operator, 'export_tangents')
1085 layout.prop(operator, 'export_colors')
1086 layout.prop(operator, 'export_attributes')
1088 col = layout.column()
1089 col.prop(operator, 'use_mesh_edges')
1090 col.prop(operator, 'use_mesh_vertices')
1093 class GLTF_PT_export_data_material(bpy.types.Panel):
1094 bl_space_type = 'FILE_BROWSER'
1095 bl_region_type = 'TOOL_PROPS'
1096 bl_label = "Material"
1097 bl_parent_id = "GLTF_PT_export_data"
1098 bl_options = {'DEFAULT_CLOSED'}
1100 @classmethod
1101 def poll(cls, context):
1102 sfile = context.space_data
1103 operator = sfile.active_operator
1104 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1106 def draw(self, context):
1107 layout = self.layout
1108 layout.use_property_split = True
1109 layout.use_property_decorate = False # No animation.
1111 sfile = context.space_data
1112 operator = sfile.active_operator
1114 layout.prop(operator, 'export_materials')
1115 col = layout.column()
1116 col.active = operator.export_materials == "EXPORT"
1117 col.prop(operator, 'export_image_format')
1118 if operator.export_image_format in ["AUTO", "JPEG", "WEBP"]:
1119 col.prop(operator, 'export_image_quality')
1120 col = layout.column()
1121 col.active = operator.export_image_format != "WEBP"
1122 col.prop(operator, "export_image_add_webp")
1123 col = layout.column()
1124 col.active = operator.export_image_format != "WEBP"
1125 col.prop(operator, "export_image_webp_fallback")
1127 class GLTF_PT_export_data_lighting(bpy.types.Panel):
1128 bl_space_type = 'FILE_BROWSER'
1129 bl_region_type = 'TOOL_PROPS'
1130 bl_label = "Lighting"
1131 bl_parent_id = "GLTF_PT_export_data"
1132 bl_options = {'DEFAULT_CLOSED'}
1134 @classmethod
1135 def poll(cls, context):
1136 sfile = context.space_data
1137 operator = sfile.active_operator
1138 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1140 def draw(self, context):
1141 layout = self.layout
1142 layout.use_property_split = True
1143 layout.use_property_decorate = False # No animation.
1145 sfile = context.space_data
1146 operator = sfile.active_operator
1148 layout.prop(operator, 'export_import_convert_lighting_mode')
1150 class GLTF_PT_export_data_shapekeys(bpy.types.Panel):
1151 bl_space_type = 'FILE_BROWSER'
1152 bl_region_type = 'TOOL_PROPS'
1153 bl_label = "Shape Keys"
1154 bl_parent_id = "GLTF_PT_export_data"
1155 bl_options = {'DEFAULT_CLOSED'}
1157 @classmethod
1158 def poll(cls, context):
1159 sfile = context.space_data
1160 operator = sfile.active_operator
1162 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1164 def draw_header(self, context):
1165 sfile = context.space_data
1166 operator = sfile.active_operator
1167 self.layout.prop(operator, "export_morph", text="")
1169 def draw(self, context):
1170 layout = self.layout
1171 layout.use_property_split = True
1172 layout.use_property_decorate = False # No animation.
1174 sfile = context.space_data
1175 operator = sfile.active_operator
1177 layout.active = operator.export_morph
1179 layout.prop(operator, 'export_morph_normal')
1180 col = layout.column()
1181 col.active = operator.export_morph_normal
1182 col.prop(operator, 'export_morph_tangent')
1185 class GLTF_PT_export_data_sk_optimize(bpy.types.Panel):
1186 bl_space_type = 'FILE_BROWSER'
1187 bl_region_type = 'TOOL_PROPS'
1188 bl_label = "Optimize Shape Keys"
1189 bl_parent_id = "GLTF_PT_export_data_shapekeys"
1190 bl_options = {'DEFAULT_CLOSED'}
1192 @classmethod
1193 def poll(cls, context):
1194 sfile = context.space_data
1195 operator = sfile.active_operator
1197 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1199 def draw(self, context):
1200 layout = self.layout
1201 layout.use_property_split = True
1202 layout.use_property_decorate = False # No animation.
1204 sfile = context.space_data
1205 operator = sfile.active_operator
1207 row = layout.row()
1208 row.prop(operator, 'export_try_sparse_sk')
1210 row = layout.row()
1211 row.active = operator.export_try_sparse_sk
1212 row.prop(operator, 'export_try_omit_sparse_sk')
1215 class GLTF_PT_export_data_skinning(bpy.types.Panel):
1216 bl_space_type = 'FILE_BROWSER'
1217 bl_region_type = 'TOOL_PROPS'
1218 bl_label = "Skinning"
1219 bl_parent_id = "GLTF_PT_export_data"
1220 bl_options = {'DEFAULT_CLOSED'}
1222 @classmethod
1223 def poll(cls, context):
1224 sfile = context.space_data
1225 operator = sfile.active_operator
1227 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1229 def draw_header(self, context):
1230 sfile = context.space_data
1231 operator = sfile.active_operator
1232 self.layout.prop(operator, "export_skins", text="")
1234 def draw(self, context):
1235 layout = self.layout
1236 layout.use_property_split = True
1237 layout.use_property_decorate = False # No animation.
1239 sfile = context.space_data
1240 operator = sfile.active_operator
1242 layout.active = operator.export_skins
1244 row = layout.row()
1245 row.prop(operator, 'export_influence_nb')
1246 row.active = not operator.export_all_influences
1247 layout.prop(operator, 'export_all_influences')
1250 class GLTF_PT_export_data_armature(bpy.types.Panel):
1251 bl_space_type = 'FILE_BROWSER'
1252 bl_region_type = 'TOOL_PROPS'
1253 bl_label = "Armature"
1254 bl_parent_id = "GLTF_PT_export_data"
1255 bl_options = {'DEFAULT_CLOSED'}
1257 @classmethod
1258 def poll(cls, context):
1259 sfile = context.space_data
1260 operator = sfile.active_operator
1262 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1264 def draw(self, context):
1265 layout = self.layout
1266 layout.use_property_split = True
1267 layout.use_property_decorate = False # No animation.
1269 sfile = context.space_data
1270 operator = sfile.active_operator
1272 layout.active = operator.export_skins
1274 layout.prop(operator, 'export_rest_position_armature')
1276 row = layout.row()
1277 row.active = operator.export_force_sampling
1278 row.prop(operator, 'export_def_bones')
1279 if operator.export_force_sampling is False and operator.export_def_bones is True:
1280 layout.label(text="Export only deformation bones is not possible when not sampling animation")
1281 row = layout.row()
1282 row.prop(operator, 'export_hierarchy_flatten_bones')
1284 class GLTF_PT_export_data_compression(bpy.types.Panel):
1285 bl_space_type = 'FILE_BROWSER'
1286 bl_region_type = 'TOOL_PROPS'
1287 bl_label = "Compression"
1288 bl_parent_id = "GLTF_PT_export_data"
1289 bl_options = {'DEFAULT_CLOSED'}
1291 def __init__(self):
1292 from .io.com import gltf2_io_draco_compression_extension
1293 self.is_draco_available = gltf2_io_draco_compression_extension.dll_exists(quiet=True)
1295 @classmethod
1296 def poll(cls, context):
1297 sfile = context.space_data
1298 operator = sfile.active_operator
1299 if operator.is_draco_available:
1300 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1302 def draw_header(self, context):
1303 sfile = context.space_data
1304 operator = sfile.active_operator
1305 self.layout.prop(operator, "export_draco_mesh_compression_enable", text="")
1307 def draw(self, context):
1308 layout = self.layout
1309 layout.use_property_split = True
1310 layout.use_property_decorate = False # No animation.
1312 sfile = context.space_data
1313 operator = sfile.active_operator
1315 layout.active = operator.export_draco_mesh_compression_enable
1316 layout.prop(operator, 'export_draco_mesh_compression_level')
1318 col = layout.column(align=True)
1319 col.prop(operator, 'export_draco_position_quantization', text="Quantize Position")
1320 col.prop(operator, 'export_draco_normal_quantization', text="Normal")
1321 col.prop(operator, 'export_draco_texcoord_quantization', text="Tex Coord")
1322 col.prop(operator, 'export_draco_color_quantization', text="Color")
1323 col.prop(operator, 'export_draco_generic_quantization', text="Generic")
1326 class GLTF_PT_export_animation(bpy.types.Panel):
1327 bl_space_type = 'FILE_BROWSER'
1328 bl_region_type = 'TOOL_PROPS'
1329 bl_label = "Animation"
1330 bl_parent_id = "FILE_PT_operator"
1331 bl_options = {'DEFAULT_CLOSED'}
1333 @classmethod
1334 def poll(cls, context):
1335 sfile = context.space_data
1336 operator = sfile.active_operator
1338 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1340 def draw_header(self, context):
1341 sfile = context.space_data
1342 operator = sfile.active_operator
1343 self.layout.prop(operator, "export_animations", text="")
1345 def draw(self, context):
1346 layout = self.layout
1347 layout.use_property_split = True
1348 layout.use_property_decorate = False # No animation.
1350 sfile = context.space_data
1351 operator = sfile.active_operator
1353 layout.active = operator.export_animations
1355 layout.prop(operator, 'export_animation_mode')
1356 if operator.export_animation_mode == "ACTIVE_ACTIONS":
1357 layout.prop(operator, 'export_nla_strips_merged_animation_name')
1359 row = layout.row()
1360 row.active = operator.export_force_sampling and operator.export_animation_mode in ['ACTIONS', 'ACTIVE_ACTIONS']
1361 row.prop(operator, 'export_bake_animation')
1362 if operator.export_animation_mode == "SCENE":
1363 layout.prop(operator, 'export_anim_scene_split_object')
1365 class GLTF_PT_export_animation_notes(bpy.types.Panel):
1366 bl_space_type = 'FILE_BROWSER'
1367 bl_region_type = 'TOOL_PROPS'
1368 bl_label = "Notes"
1369 bl_parent_id = "GLTF_PT_export_animation"
1370 bl_options = {'DEFAULT_CLOSED'}
1372 @classmethod
1373 def poll(cls, context):
1374 sfile = context.space_data
1375 operator = sfile.active_operator
1377 return operator.bl_idname == "EXPORT_SCENE_OT_gltf" and \
1378 operator.export_animation_mode in ["NLA_TRACKS", "SCENE"]
1380 def draw(self, context):
1381 operator = context.space_data.active_operator
1382 layout = self.layout
1383 if operator.export_animation_mode == "SCENE":
1384 layout.label(text="Scene mode uses full bake mode:")
1385 layout.label(text="- sampling is active")
1386 layout.label(text="- baking all objects is active")
1387 layout.label(text="- Using scene frame range")
1388 elif operator.export_animation_mode == "NLA_TRACKS":
1389 layout.label(text="Track mode uses full bake mode:")
1390 layout.label(text="- sampling is active")
1391 layout.label(text="- baking all objects is active")
1393 class GLTF_PT_export_animation_ranges(bpy.types.Panel):
1394 bl_space_type = 'FILE_BROWSER'
1395 bl_region_type = 'TOOL_PROPS'
1396 bl_label = "Rest & Ranges"
1397 bl_parent_id = "GLTF_PT_export_animation"
1398 bl_options = {'DEFAULT_CLOSED'}
1400 @classmethod
1401 def poll(cls, context):
1402 sfile = context.space_data
1403 operator = sfile.active_operator
1405 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1407 def draw(self, context):
1408 layout = self.layout
1409 layout.use_property_split = True
1410 layout.use_property_decorate = False # No animation.
1412 sfile = context.space_data
1413 operator = sfile.active_operator
1415 layout.active = operator.export_animations
1417 layout.prop(operator, 'export_current_frame')
1418 row = layout.row()
1419 row.active = operator.export_animation_mode in ['ACTIONS', 'ACTIVE_ACTIONS', 'NLA_TRACKS']
1420 row.prop(operator, 'export_frame_range')
1421 layout.prop(operator, 'export_anim_slide_to_zero')
1422 row = layout.row()
1423 row.active = operator.export_animation_mode in ['ACTIONS', 'ACTIVE_ACTIONS', 'NLA_TRACKS']
1424 layout.prop(operator, 'export_negative_frame')
1426 class GLTF_PT_export_animation_armature(bpy.types.Panel):
1427 bl_space_type = 'FILE_BROWSER'
1428 bl_region_type = 'TOOL_PROPS'
1429 bl_label = "Armature"
1430 bl_parent_id = "GLTF_PT_export_animation"
1431 bl_options = {'DEFAULT_CLOSED'}
1433 @classmethod
1434 def poll(cls, context):
1435 sfile = context.space_data
1436 operator = sfile.active_operator
1438 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1440 def draw(self, context):
1441 layout = self.layout
1442 layout.use_property_split = True
1443 layout.use_property_decorate = False # No animation.
1445 sfile = context.space_data
1446 operator = sfile.active_operator
1448 layout.active = operator.export_animations
1450 layout.prop(operator, 'export_anim_single_armature')
1451 layout.prop(operator, 'export_reset_pose_bones')
1453 class GLTF_PT_export_animation_shapekeys(bpy.types.Panel):
1454 bl_space_type = 'FILE_BROWSER'
1455 bl_region_type = 'TOOL_PROPS'
1456 bl_label = "Shape Keys Animation"
1457 bl_parent_id = "GLTF_PT_export_animation"
1458 bl_options = {'DEFAULT_CLOSED'}
1460 @classmethod
1461 def poll(cls, context):
1462 sfile = context.space_data
1463 operator = sfile.active_operator
1465 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1467 def draw_header(self, context):
1468 sfile = context.space_data
1469 operator = sfile.active_operator
1470 self.layout.active = operator.export_animations and operator.export_morph
1471 self.layout.prop(operator, "export_morph_animation", text="")
1473 def draw(self, context):
1474 layout = self.layout
1475 layout.use_property_split = True
1476 layout.use_property_decorate = False # No animation.
1478 sfile = context.space_data
1479 operator = sfile.active_operator
1481 layout.active = operator.export_animations
1483 layout.prop(operator, 'export_morph_reset_sk_data')
1486 class GLTF_PT_export_animation_sampling(bpy.types.Panel):
1487 bl_space_type = 'FILE_BROWSER'
1488 bl_region_type = 'TOOL_PROPS'
1489 bl_label = "Sampling Animations"
1490 bl_parent_id = "GLTF_PT_export_animation"
1491 bl_options = {'DEFAULT_CLOSED'}
1493 @classmethod
1494 def poll(cls, context):
1495 sfile = context.space_data
1496 operator = sfile.active_operator
1498 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1500 def draw_header(self, context):
1501 sfile = context.space_data
1502 operator = sfile.active_operator
1503 self.layout.active = operator.export_animations and operator.export_animation_mode in ['ACTIONS', 'ACTIVE_ACTIONS']
1504 self.layout.prop(operator, "export_force_sampling", text="")
1506 def draw(self, context):
1507 layout = self.layout
1508 layout.use_property_split = True
1509 layout.use_property_decorate = False # No animation.
1511 sfile = context.space_data
1512 operator = sfile.active_operator
1514 layout.active = operator.export_animations
1516 layout.prop(operator, 'export_frame_step')
1519 class GLTF_PT_export_animation_optimize(bpy.types.Panel):
1520 bl_space_type = 'FILE_BROWSER'
1521 bl_region_type = 'TOOL_PROPS'
1522 bl_label = "Optimize Animations"
1523 bl_parent_id = "GLTF_PT_export_animation"
1524 bl_options = {'DEFAULT_CLOSED'}
1526 @classmethod
1527 def poll(cls, context):
1528 sfile = context.space_data
1529 operator = sfile.active_operator
1531 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1533 def draw(self, context):
1534 layout = self.layout
1535 layout.use_property_split = True
1536 layout.use_property_decorate = False # No animation.
1538 sfile = context.space_data
1539 operator = sfile.active_operator
1541 layout.active = operator.export_animations
1543 layout.prop(operator, 'export_optimize_animation_size')
1545 row = layout.row()
1546 row.prop(operator, 'export_optimize_animation_keep_anim_armature')
1548 row = layout.row()
1549 row.prop(operator, 'export_optimize_animation_keep_anim_object')
1552 class GLTF_PT_export_user_extensions(bpy.types.Panel):
1553 bl_space_type = 'FILE_BROWSER'
1554 bl_region_type = 'TOOL_PROPS'
1555 bl_label = "Exporter Extensions"
1556 bl_parent_id = "FILE_PT_operator"
1557 bl_options = {'DEFAULT_CLOSED'}
1559 @classmethod
1560 def poll(cls, context):
1561 sfile = context.space_data
1562 operator = sfile.active_operator
1564 return operator.bl_idname == "EXPORT_SCENE_OT_gltf" and operator.has_active_exporter_extensions
1566 def draw(self, context):
1567 layout = self.layout
1568 layout.use_property_split = True
1569 layout.use_property_decorate = False # No animation.
1571 class GLTF_PT_import_user_extensions(bpy.types.Panel):
1572 bl_space_type = 'FILE_BROWSER'
1573 bl_region_type = 'TOOL_PROPS'
1574 bl_label = "Importer Extensions"
1575 bl_parent_id = "FILE_PT_operator"
1576 bl_options = {'DEFAULT_CLOSED'}
1578 @classmethod
1579 def poll(cls, context):
1580 sfile = context.space_data
1581 operator = sfile.active_operator
1582 return operator.bl_idname == "IMPORT_SCENE_OT_gltf" and operator.has_active_importer_extensions
1584 def draw(self, context):
1585 layout = self.layout
1586 layout.use_property_split = True
1587 layout.use_property_decorate = False # No animation.
1589 class ExportGLTF2(bpy.types.Operator, ExportGLTF2_Base, ExportHelper):
1590 """Export scene as glTF 2.0 file"""
1591 bl_idname = 'export_scene.gltf'
1592 bl_label = 'Export glTF 2.0'
1594 filename_ext = ''
1596 filter_glob: StringProperty(default='*.glb', options={'HIDDEN'})
1599 def menu_func_export(self, context):
1600 self.layout.operator(ExportGLTF2.bl_idname, text='glTF 2.0 (.glb/.gltf)')
1603 class ImportGLTF2(Operator, ConvertGLTF2_Base, ImportHelper):
1604 """Load a glTF 2.0 file"""
1605 bl_idname = 'import_scene.gltf'
1606 bl_label = 'Import glTF 2.0'
1607 bl_options = {'REGISTER', 'UNDO'}
1609 filter_glob: StringProperty(default="*.glb;*.gltf", options={'HIDDEN'})
1611 files: CollectionProperty(
1612 name="File Path",
1613 type=bpy.types.OperatorFileListElement,
1616 loglevel: IntProperty(
1617 name='Log Level',
1618 description="Log Level")
1620 import_pack_images: BoolProperty(
1621 name='Pack Images',
1622 description='Pack all images into .blend file',
1623 default=True
1626 merge_vertices: BoolProperty(
1627 name='Merge Vertices',
1628 description=(
1629 'The glTF format requires discontinuous normals, UVs, and '
1630 'other vertex attributes to be stored as separate vertices, '
1631 'as required for rendering on typical graphics hardware. '
1632 'This option attempts to combine co-located vertices where possible. '
1633 'Currently cannot combine verts with different normals'
1635 default=False,
1638 import_shading: EnumProperty(
1639 name="Shading",
1640 items=(("NORMALS", "Use Normal Data", ""),
1641 ("FLAT", "Flat Shading", ""),
1642 ("SMOOTH", "Smooth Shading", "")),
1643 description="How normals are computed during import",
1644 default="NORMALS")
1646 bone_heuristic: EnumProperty(
1647 name="Bone Dir",
1648 items=(
1649 ("BLENDER", "Blender (best for import/export round trip)",
1650 "Good for re-importing glTFs exported from Blender, "
1651 "and re-exporting glTFs to glTFs after Blender editing"
1652 "Bone tips are placed on their local +Y axis (in glTF space)"),
1653 ("TEMPERANCE", "Temperance (average)",
1654 "Decent all-around strategy. "
1655 "A bone with one child has its tip placed on the local axis "
1656 "closest to its child"),
1657 ("FORTUNE", "Fortune (may look better, less accurate)",
1658 "Might look better than Temperance, but also might have errors. "
1659 "A bone with one child has its tip placed at its child's root. "
1660 "Non-uniform scalings may get messed up though, so beware"),
1662 description="Heuristic for placing bones. Tries to make bones pretty",
1663 default="BLENDER",
1666 guess_original_bind_pose: BoolProperty(
1667 name='Guess Original Bind Pose',
1668 description=(
1669 'Try to guess the original bind pose for skinned meshes from '
1670 'the inverse bind matrices. '
1671 'When off, use default/rest pose as bind pose'
1673 default=True,
1676 import_webp_texture: BoolProperty(
1677 name='Import Webp textures',
1678 description=(
1679 "If a texture exists in webp format,"
1680 "loads the webp texture instead of the fallback png/jpg one"
1682 default=False,
1685 def draw(self, context):
1686 layout = self.layout
1688 layout.use_property_split = True
1689 layout.use_property_decorate = False # No animation.
1691 layout.prop(self, 'import_pack_images')
1692 layout.prop(self, 'merge_vertices')
1693 layout.prop(self, 'import_shading')
1694 layout.prop(self, 'guess_original_bind_pose')
1695 layout.prop(self, 'bone_heuristic')
1696 layout.prop(self, 'export_import_convert_lighting_mode')
1697 layout.prop(self, 'import_webp_texture')
1699 def invoke(self, context, event):
1700 import sys
1701 preferences = bpy.context.preferences
1702 for addon_name in preferences.addons.keys():
1703 try:
1704 if hasattr(sys.modules[addon_name], 'glTF2ImportUserExtension') or hasattr(sys.modules[addon_name], 'glTF2ImportUserExtensions'):
1705 importer_extension_panel_unregister_functors.append(sys.modules[addon_name].register_panel())
1706 except Exception:
1707 pass
1709 self.has_active_importer_extensions = len(importer_extension_panel_unregister_functors) > 0
1710 return ImportHelper.invoke(self, context, event)
1712 def execute(self, context):
1713 return self.import_gltf2(context)
1715 def import_gltf2(self, context):
1716 import os
1718 self.set_debug_log()
1719 import_settings = self.as_keywords()
1721 user_extensions = []
1723 import sys
1724 preferences = bpy.context.preferences
1725 for addon_name in preferences.addons.keys():
1726 try:
1727 module = sys.modules[addon_name]
1728 except Exception:
1729 continue
1730 if hasattr(module, 'glTF2ImportUserExtension'):
1731 extension_ctor = module.glTF2ImportUserExtension
1732 user_extensions.append(extension_ctor())
1733 import_settings['import_user_extensions'] = user_extensions
1735 if self.files:
1736 # Multiple file import
1737 ret = {'CANCELLED'}
1738 dirname = os.path.dirname(self.filepath)
1739 for file in self.files:
1740 path = os.path.join(dirname, file.name)
1741 if self.unit_import(path, import_settings) == {'FINISHED'}:
1742 ret = {'FINISHED'}
1743 return ret
1744 else:
1745 # Single file import
1746 return self.unit_import(self.filepath, import_settings)
1748 def unit_import(self, filename, import_settings):
1749 import time
1750 from .io.imp.gltf2_io_gltf import glTFImporter, ImportError
1751 from .blender.imp.gltf2_blender_gltf import BlenderGlTF
1753 try:
1754 gltf_importer = glTFImporter(filename, import_settings)
1755 gltf_importer.read()
1756 gltf_importer.checks()
1758 print("Data are loaded, start creating Blender stuff")
1760 start_time = time.time()
1761 BlenderGlTF.create(gltf_importer)
1762 elapsed_s = "{:.2f}s".format(time.time() - start_time)
1763 print("glTF import finished in " + elapsed_s)
1765 gltf_importer.log.removeHandler(gltf_importer.log_handler)
1767 return {'FINISHED'}
1769 except ImportError as e:
1770 self.report({'ERROR'}, e.args[0])
1771 return {'CANCELLED'}
1773 def set_debug_log(self):
1774 import logging
1775 if bpy.app.debug_value == 0:
1776 self.loglevel = logging.CRITICAL
1777 elif bpy.app.debug_value == 1:
1778 self.loglevel = logging.ERROR
1779 elif bpy.app.debug_value == 2:
1780 self.loglevel = logging.WARNING
1781 elif bpy.app.debug_value == 3:
1782 self.loglevel = logging.INFO
1783 else:
1784 self.loglevel = logging.NOTSET
1787 def gltf_variant_ui_update(self, context):
1788 from .blender.com.gltf2_blender_ui import variant_register, variant_unregister
1789 if self.KHR_materials_variants_ui is True:
1790 # register all needed types
1791 variant_register()
1792 else:
1793 variant_unregister()
1795 def gltf_animation_ui_update(self, context):
1796 from .blender.com.gltf2_blender_ui import anim_ui_register, anim_ui_unregister
1797 if self.animation_ui is True:
1798 # register all needed types
1799 anim_ui_register()
1800 else:
1801 anim_ui_unregister()
1803 class GLTF_AddonPreferences(bpy.types.AddonPreferences):
1804 bl_idname = __package__
1806 settings_node_ui : bpy.props.BoolProperty(
1807 default= False,
1808 description="Displays glTF Material Output node in Shader Editor (Menu Add > Output)"
1811 KHR_materials_variants_ui : bpy.props.BoolProperty(
1812 default= False,
1813 description="Displays glTF UI to manage material variants",
1814 update=gltf_variant_ui_update
1817 animation_ui: bpy.props.BoolProperty(
1818 default=False,
1819 description="Display glTF UI to manage animations",
1820 update=gltf_animation_ui_update
1823 def draw(self, context):
1824 layout = self.layout
1825 row = layout.row()
1826 row.prop(self, "settings_node_ui", text="Shader Editor Add-ons")
1827 row.prop(self, "KHR_materials_variants_ui", text="Material Variants")
1828 row.prop(self, "animation_ui", text="Animation UI")
1830 def menu_func_import(self, context):
1831 self.layout.operator(ImportGLTF2.bl_idname, text='glTF 2.0 (.glb/.gltf)')
1834 classes = (
1835 ExportGLTF2,
1836 GLTF_PT_export_main,
1837 GLTF_PT_export_include,
1838 GLTF_PT_export_transform,
1839 GLTF_PT_export_data,
1840 GLTF_PT_export_data_scene,
1841 GLTF_PT_export_data_mesh,
1842 GLTF_PT_export_data_material,
1843 GLTF_PT_export_data_shapekeys,
1844 GLTF_PT_export_data_sk_optimize,
1845 GLTF_PT_export_data_armature,
1846 GLTF_PT_export_data_skinning,
1847 GLTF_PT_export_data_lighting,
1848 GLTF_PT_export_data_compression,
1849 GLTF_PT_export_animation,
1850 GLTF_PT_export_animation_notes,
1851 GLTF_PT_export_animation_ranges,
1852 GLTF_PT_export_animation_armature,
1853 GLTF_PT_export_animation_shapekeys,
1854 GLTF_PT_export_animation_sampling,
1855 GLTF_PT_export_animation_optimize,
1856 GLTF_PT_export_user_extensions,
1857 ImportGLTF2,
1858 GLTF_PT_import_user_extensions,
1859 GLTF_AddonPreferences
1863 def register():
1864 from .blender.com import gltf2_blender_ui as blender_ui
1865 for c in classes:
1866 bpy.utils.register_class(c)
1867 # bpy.utils.register_module(__name__)
1869 blender_ui.register()
1870 if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True:
1871 blender_ui.variant_register()
1872 if bpy.context.preferences.addons['io_scene_gltf2'].preferences.animation_ui is True:
1873 blender_ui.anim_ui_register()
1875 # add to the export / import menu
1876 bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
1877 bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
1880 def unregister():
1881 from .blender.com import gltf2_blender_ui as blender_ui
1882 blender_ui.unregister()
1883 if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True:
1884 blender_ui.variant_unregister()
1886 for c in classes:
1887 bpy.utils.unregister_class(c)
1888 for f in exporter_extension_panel_unregister_functors:
1890 exporter_extension_panel_unregister_functors.clear()
1892 for f in importer_extension_panel_unregister_functors:
1894 importer_extension_panel_unregister_functors.clear()
1896 # bpy.utils.unregister_module(__name__)
1898 # remove from the export / import menu
1899 bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
1900 bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)