Export_3ds: Added distance cue chunk export
[blender-addons.git] / io_scene_gltf2 / __init__.py
bloba2d5955d293c8f0ed74449c1cec3ef92f3338ad1
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, 2, 23),
9 'blender': (4, 2, 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 FloatProperty,
52 CollectionProperty)
53 from bpy.types import Operator
54 from bpy_extras.io_utils import ImportHelper, ExportHelper, poll_file_object_drop
58 # Functions / Classes.
61 exporter_extension_panel_unregister_functors = []
62 importer_extension_panel_unregister_functors = []
65 def ensure_filepath_matches_export_format(filepath, export_format):
66 import os
67 filename = os.path.basename(filepath)
68 if not filename:
69 return filepath
71 stem, ext = os.path.splitext(filename)
72 if stem.startswith('.') and not ext:
73 stem, ext = '', stem
75 desired_ext = '.glb' if export_format == 'GLB' else '.gltf'
76 ext_lower = ext.lower()
77 if ext_lower not in ['.glb', '.gltf']:
78 return filepath + desired_ext
79 elif ext_lower != desired_ext:
80 filepath = filepath[:-len(ext)] # strip off ext
81 return filepath + desired_ext
82 else:
83 return filepath
86 def on_export_format_changed(self, context):
87 # Update the filename in the file browser when the format (.glb/.gltf)
88 # changes
89 sfile = context.space_data
90 if not isinstance(sfile, bpy.types.SpaceFileBrowser):
91 return
92 if not sfile.active_operator:
93 return
94 if sfile.active_operator.bl_idname != "EXPORT_SCENE_OT_gltf":
95 return
97 sfile.params.filename = ensure_filepath_matches_export_format(
98 sfile.params.filename,
99 self.export_format,
102 # Also change the filter
103 sfile.params.filter_glob = '*.glb' if self.export_format == 'GLB' else '*.gltf'
104 # Force update of file list, because update the filter does not update the real file list
105 bpy.ops.file.refresh()
107 def on_export_action_filter_changed(self, context):
108 if self.export_action_filter is True:
109 bpy.types.Scene.gltf_action_filter = bpy.props.CollectionProperty(type=GLTF2_filter_action)
110 bpy.types.Scene.gltf_action_filter_active = bpy.props.IntProperty()
112 for action in bpy.data.actions:
113 if id(action) not in [id(item.action) for item in bpy.data.scenes[0].gltf_action_filter]:
114 item = bpy.data.scenes[0].gltf_action_filter.add()
115 item.keep = True
116 item.action = action
118 else:
119 bpy.data.scenes[0].gltf_action_filter.clear()
120 del bpy.types.Scene.gltf_action_filter
121 del bpy.types.Scene.gltf_action_filter_active
124 def get_format_items(scene, context):
127 items = (('GLB', 'glTF Binary (.glb)',
128 'Exports a single file, with all data packed in binary form. '
129 'Most efficient and portable, but more difficult to edit later'),
130 ('GLTF_SEPARATE', 'glTF Separate (.gltf + .bin + textures)',
131 'Exports multiple files, with separate JSON, binary and texture data. '
132 'Easiest to edit later'))
134 if bpy.context.preferences.addons['io_scene_gltf2'].preferences \
135 and "allow_embedded_format" in bpy.context.preferences.addons['io_scene_gltf2'].preferences \
136 and bpy.context.preferences.addons['io_scene_gltf2'].preferences['allow_embedded_format']:
137 # At initialization, the preferences are not yet loaded
138 # The second line check is needed until the PR is merge in Blender, for github CI tests
139 items += (('GLTF_EMBEDDED', 'glTF Embedded (.gltf)',
140 'Exports a single file, with all data packed in JSON. '
141 'Less efficient than binary, but easier to edit later'
144 return items
147 def is_draco_available():
148 # Initialize on first use
149 if not hasattr(is_draco_available, "draco_exists"):
150 from .io.com import gltf2_io_draco_compression_extension
151 is_draco_available.draco_exists = gltf2_io_draco_compression_extension.dll_exists()
153 return is_draco_available.draco_exists
156 class ConvertGLTF2_Base:
157 """Base class containing options that should be exposed during both import and export."""
159 export_import_convert_lighting_mode: EnumProperty(
160 name='Lighting Mode',
161 items=(
162 ('SPEC', 'Standard', 'Physically-based glTF lighting units (cd, lx, nt)'),
163 ('COMPAT', 'Unitless', 'Non-physical, unitless lighting. Useful when exposure controls are not available'),
164 ('RAW', 'Raw (Deprecated)', 'Blender lighting strengths with no conversion'),
166 description='Optional backwards compatibility for non-standard render engines. Applies to lights',# TODO: and emissive materials',
167 default='SPEC'
170 class ExportGLTF2_Base(ConvertGLTF2_Base):
171 # TODO: refactor to avoid boilerplate
173 bl_options = {'PRESET'}
175 # Don't use export_ prefix here, I don't want it to be saved with other export settings
176 gltf_export_id: StringProperty(
177 name='Identifier',
178 description=(
179 'Identifier of caller (in case of add-on calling this exporter). '
180 'Can be useful in case of Extension added by other add-ons'
182 default=''
185 # gltfpack properties
186 export_use_gltfpack: BoolProperty(
187 name='Use Gltfpack',
188 description='Use gltfpack to simplify the mesh and/or compress its textures',
189 default=False,
192 export_gltfpack_tc: BoolProperty(
193 name='KTX2 Compression',
194 description='Convert all textures to KTX2 with BasisU supercompression',
195 default=True,
198 export_gltfpack_tq: IntProperty(
199 name='Texture Encoding Quality',
200 description='Texture encoding quality',
201 default=8,
202 min=1,
203 max=10,
206 export_gltfpack_si: FloatProperty(
207 name='Mesh Simplification Ratio',
208 description='Simplify meshes targeting triangle count ratio',
209 default=1.0,
210 min=0.0,
211 max=1.0,
214 export_gltfpack_sa: BoolProperty(
215 name='Aggressive Mesh Simplification',
216 description='Aggressively simplify to the target ratio disregarding quality',
217 default=False,
220 export_gltfpack_slb: BoolProperty(
221 name='Lock Mesh Border Vertices',
222 description='Lock border vertices during simplification to avoid gaps on connected meshes',
223 default=False,
226 export_gltfpack_vp: IntProperty(
227 name='Position Quantization',
228 description='Use N-bit quantization for positions',
229 default=14,
230 min=1,
231 max=16,
234 export_gltfpack_vt: IntProperty(
235 name='Texture Coordinate Quantization',
236 description='Use N-bit quantization for texture coordinates',
237 default=12,
238 min=1,
239 max=16,
242 export_gltfpack_vn: IntProperty(
243 name='Normal/Tangent Quantization',
244 description='Use N-bit quantization for normals and tangents',
245 default=8,
246 min=1,
247 max=16,
250 export_gltfpack_vc: IntProperty(
251 name='Vertex Color Quantization',
252 description='Use N-bit quantization for colors',
253 default=8,
254 min=1,
255 max=16,
258 export_gltfpack_vpi: EnumProperty(
259 name='Vertex Position Attributes',
260 description='Type to use for vertex position attributes',
261 items=(('Integer', 'Integer', 'Use integer attributes for positions'),
262 ('Normalized', 'Normalized', 'Use normalized attributes for positions'),
263 ('Floating-point', 'Floating-point', 'Use floating-point attributes for positions')),
264 default='Integer',
267 export_gltfpack_noq: BoolProperty(
268 name='Disable Quantization',
269 description='Disable quantization; produces much larger glTF files with no extensions',
270 default=True,
273 # TODO: some stuff in Textures
275 # TODO: Animations
277 # TODO: Scene
279 # TODO: some stuff in Miscellaneous
281 export_format: EnumProperty(
282 name='Format',
283 items=get_format_items,
284 description=(
285 'Output format. Binary is most efficient, '
286 'but JSON may be easier to edit later'
288 default=0, #Warning => If you change the default, need to change the default filter too
289 update=on_export_format_changed,
292 ui_tab: EnumProperty(
293 items=(('GENERAL', "General", "General settings"),
294 ('MESHES', "Meshes", "Mesh settings"),
295 ('OBJECTS', "Objects", "Object settings"),
296 ('ANIMATION', "Animation", "Animation settings")),
297 name="ui_tab",
298 description="Export setting categories",
301 export_copyright: StringProperty(
302 name='Copyright',
303 description='Legal rights and conditions for the model',
304 default=''
307 export_image_format: EnumProperty(
308 name='Images',
309 items=(('AUTO', 'Automatic',
310 'Save PNGs as PNGs, JPEGs as JPEGs, WebPs as WebPs. '
311 'For other formats, use PNG'),
312 ('JPEG', 'JPEG Format (.jpg)',
313 'Save images as JPEGs. (Images that need alpha are saved as PNGs though.) '
314 'Be aware of a possible loss in quality'),
315 ('WEBP', 'WebP Format',
316 'Save images as WebPs as main image (no fallback)'),
317 ('NONE', 'None',
318 'Don\'t export images'),
320 description=(
321 'Output format for images. PNG is lossless and generally preferred, but JPEG might be preferable for web '
322 'applications due to the smaller file size. Alternatively they can be omitted if they are not needed'
324 default='AUTO'
327 export_image_add_webp: BoolProperty(
328 name='Create WebP',
329 description=(
330 "Creates WebP textures for every texture. "
331 "For already WebP textures, nothing happens"
333 default=False
336 export_image_webp_fallback: BoolProperty(
337 name='WebP fallback',
338 description=(
339 "For all WebP textures, create a PNG fallback texture"
341 default=False
344 export_texture_dir: StringProperty(
345 name='Textures',
346 description='Folder to place texture files in. Relative to the .gltf file',
347 default='',
350 # Keep for back compatibility
351 export_jpeg_quality: IntProperty(
352 name='JPEG quality',
353 description='Quality of JPEG export',
354 default=75,
355 min=0,
356 max=100
359 # Keep for back compatibility
360 export_image_quality: IntProperty(
361 name='Image quality',
362 description='Quality of image export',
363 default=75,
364 min=0,
365 max=100
368 export_keep_originals: BoolProperty(
369 name='Keep original',
370 description=('Keep original textures files if possible. '
371 'WARNING: if you use more than one texture, '
372 'where pbr standard requires only one, only one texture will be used. '
373 'This can lead to unexpected results'
375 default=False,
378 export_texcoords: BoolProperty(
379 name='UVs',
380 description='Export UVs (texture coordinates) with meshes',
381 default=True
384 export_normals: BoolProperty(
385 name='Normals',
386 description='Export vertex normals with meshes',
387 default=True
390 export_gn_mesh: BoolProperty(
391 name='Geometry Nodes Instances (Experimental)',
392 description='Export Geometry nodes instance meshes',
393 default=False
396 export_draco_mesh_compression_enable: BoolProperty(
397 name='Draco mesh compression',
398 description='Compress mesh using Draco',
399 default=False
402 export_draco_mesh_compression_level: IntProperty(
403 name='Compression level',
404 description='Compression level (0 = most speed, 6 = most compression, higher values currently not supported)',
405 default=6,
406 min=0,
407 max=10
410 export_draco_position_quantization: IntProperty(
411 name='Position quantization bits',
412 description='Quantization bits for position values (0 = no quantization)',
413 default=14,
414 min=0,
415 max=30
418 export_draco_normal_quantization: IntProperty(
419 name='Normal quantization bits',
420 description='Quantization bits for normal values (0 = no quantization)',
421 default=10,
422 min=0,
423 max=30
426 export_draco_texcoord_quantization: IntProperty(
427 name='Texcoord quantization bits',
428 description='Quantization bits for texture coordinate values (0 = no quantization)',
429 default=12,
430 min=0,
431 max=30
434 export_draco_color_quantization: IntProperty(
435 name='Color quantization bits',
436 description='Quantization bits for color values (0 = no quantization)',
437 default=10,
438 min=0,
439 max=30
442 export_draco_generic_quantization: IntProperty(
443 name='Generic quantization bits',
444 description='Quantization bits for generic values like weights or joints (0 = no quantization)',
445 default=12,
446 min=0,
447 max=30
450 export_tangents: BoolProperty(
451 name='Tangents',
452 description='Export vertex tangents with meshes',
453 default=False
456 export_materials: EnumProperty(
457 name='Materials',
458 items=(('EXPORT', 'Export',
459 'Export all materials used by included objects'),
460 ('PLACEHOLDER', 'Placeholder',
461 'Do not export materials, but write multiple primitive groups per mesh, keeping material slot information'),
462 ('NONE', 'No export',
463 'Do not export materials, and combine mesh primitive groups, losing material slot information')),
464 description='Export materials',
465 default='EXPORT'
468 export_unused_images: BoolProperty(
469 name='Unused images',
470 description='Export images not assigned to any material',
471 default=False)
473 export_unused_textures: BoolProperty(
474 name='Prepare Unused textures',
475 description=(
476 'Export image texture nodes not assigned to any material. '
477 'This feature is not standard and needs an external extension to be included in the glTF file'
479 default=False)
481 export_colors: BoolProperty(
482 name='Dummy',
483 description='Keep for compatibility only',
484 default=True
487 export_attributes: BoolProperty(
488 name='Attributes',
489 description='Export Attributes (when starting with underscore)',
490 default=False
493 use_mesh_edges: BoolProperty(
494 name='Loose Edges',
495 description=(
496 'Export loose edges as lines, using the material from the first material slot'
498 default=False,
501 use_mesh_vertices: BoolProperty(
502 name='Loose Points',
503 description=(
504 'Export loose points as glTF points, using the material from the first material slot'
506 default=False,
509 export_cameras: BoolProperty(
510 name='Cameras',
511 description='Export cameras',
512 default=False
515 use_selection: BoolProperty(
516 name='Selected Objects',
517 description='Export selected objects only',
518 default=False
521 use_visible: BoolProperty(
522 name='Visible Objects',
523 description='Export visible objects only',
524 default=False
527 use_renderable: BoolProperty(
528 name='Renderable Objects',
529 description='Export renderable objects only',
530 default=False
533 use_active_collection_with_nested: BoolProperty(
534 name='Include Nested Collections',
535 description='Include active collection and nested collections',
536 default=True
539 use_active_collection: BoolProperty(
540 name='Active Collection',
541 description='Export objects in the active collection only',
542 default=False
545 use_active_scene: BoolProperty(
546 name='Active Scene',
547 description='Export active scene only',
548 default=False
551 collection: StringProperty(
552 name="Source Collection",
553 description="Export only objects from this collection (and its children)",
554 default="",
557 export_extras: BoolProperty(
558 name='Custom Properties',
559 description='Export custom properties as glTF extras',
560 default=False
563 export_yup: BoolProperty(
564 name='+Y Up',
565 description='Export using glTF convention, +Y up',
566 default=True
569 export_apply: BoolProperty(
570 name='Apply Modifiers',
571 description='Apply modifiers (excluding Armatures) to mesh objects -'
572 'WARNING: prevents exporting shape keys',
573 default=False
576 export_shared_accessors: BoolProperty(
577 name='Shared Accessors',
578 description='Export Primitives using shared accessors for attributes',
579 default=False
582 export_animations: BoolProperty(
583 name='Animations',
584 description='Exports active actions and NLA tracks as glTF animations',
585 default=True
588 export_frame_range: BoolProperty(
589 name='Limit to Playback Range',
590 description='Clips animations to selected playback range',
591 default=False
594 export_frame_step: IntProperty(
595 name='Sampling Rate',
596 description='How often to evaluate animated values (in frames)',
597 default=1,
598 min=1,
599 max=120
602 export_force_sampling: BoolProperty(
603 name='Always Sample Animations',
604 description='Apply sampling to all animations',
605 default=True
608 export_pointer_animation: BoolProperty(
609 name='Export Animation Pointer (Experimental)',
610 description='Export material, Light & Camera animation as Animation Pointer',
611 default=False
614 export_animation_mode: EnumProperty(
615 name='Animation mode',
616 items=(('ACTIONS', 'Actions',
617 'Export actions (actives and on NLA tracks) as separate animations'),
618 ('ACTIVE_ACTIONS', 'Active actions merged',
619 'All the currently assigned actions become one glTF animation'),
620 ('BROADCAST', 'Broadcast actions',
621 'Broadcast all compatible actions to all objects. '
622 'Animated objects will get all actions compatible with them, '
623 'others will get no animation at all'),
624 ('NLA_TRACKS', 'NLA Tracks',
625 'Export individual NLA Tracks as separate animation'),
626 ('SCENE', 'Scene',
627 'Export baked scene as a single animation')
629 description='Export Animation mode',
630 default='ACTIONS'
633 export_nla_strips_merged_animation_name: StringProperty(
634 name='Merged Animation Name',
635 description=(
636 "Name of single glTF animation to be exported"
638 default='Animation'
641 export_def_bones: BoolProperty(
642 name='Export Deformation Bones Only',
643 description='Export Deformation bones only',
644 default=False
647 export_hierarchy_flatten_bones: BoolProperty(
648 name='Flatten Bone Hierarchy',
649 description='Flatten Bone Hierarchy. Useful in case of non decomposable transformation matrix',
650 default=False
653 export_hierarchy_flatten_objs: BoolProperty(
654 name='Flatten Object Hierarchy',
655 description='Flatten Object Hierarchy. Useful in case of non decomposable transformation matrix',
656 default=False
659 export_armature_object_remove: BoolProperty(
660 name='Remove Armature Object',
661 description=(
662 'Remove Armature object if possible. '
663 'If Armature has multiple root bones, object will not be removed'
665 default=False
668 export_leaf_bone: BoolProperty(
669 name='Add Leaf Bones',
670 description=(
671 'Append a final bone to the end of each chain to specify last bone length '
672 '(use this when you intend to edit the armature from exported data)'
674 default=False
677 export_optimize_animation_size: BoolProperty(
678 name='Optimize Animation Size',
679 description=(
680 "Reduce exported file size by removing duplicate keyframes"
682 default=True
685 export_optimize_animation_keep_anim_armature: BoolProperty(
686 name='Force keeping channels for bones',
687 description=(
688 "If all keyframes are identical in a rig, "
689 "force keeping the minimal animation. "
690 "When off, all possible channels for "
691 "the bones will be exported, even if empty "
692 "(minimal animation, 2 keyframes)"
694 default=True
697 export_optimize_animation_keep_anim_object: BoolProperty(
698 name='Force keeping channel for objects',
699 description=(
700 "If all keyframes are identical for object transformations, "
701 "force keeping the minimal animation"
703 default=False
706 export_optimize_armature_disable_viewport: BoolProperty(
707 name='Disable viewport if possible',
708 description=(
709 "When exporting armature, disable viewport for other objects, "
710 "for performance. Drivers on shape keys for skined meshes prevent this optimization for now"
712 default=False
715 export_negative_frame: EnumProperty(
716 name='Negative Frames',
717 items=(('SLIDE', 'Slide',
718 'Slide animation to start at frame 0'),
719 ('CROP', 'Crop',
720 'Keep only frames above frame 0'),
722 description='Negative Frames are slid or cropped',
723 default='SLIDE'
726 export_anim_slide_to_zero: BoolProperty(
727 name='Set all glTF Animation starting at 0',
728 description=(
729 "Set all glTF animation starting at 0.0s. "
730 "Can be useful for looping animations"
732 default=False
735 export_bake_animation: BoolProperty(
736 name='Bake All Objects Animations',
737 description=(
738 "Force exporting animation on every object. "
739 "Can be useful when using constraints or driver. "
740 "Also useful when exporting only selection"
742 default=False
745 export_anim_single_armature: BoolProperty(
746 name='Export all Armature Actions',
747 description=(
748 "Export all actions, bound to a single armature. "
749 "WARNING: Option does not support exports including multiple armatures"
751 default=True
754 export_reset_pose_bones: BoolProperty(
755 name='Reset pose bones between actions',
756 description=(
757 "Reset pose bones between each action exported. "
758 "This is needed when some bones are not keyed on some animations"
760 default=True
763 export_current_frame: BoolProperty(
764 name='Use Current Frame as Object Rest Transformations',
765 description=(
766 'Export the scene in the current animation frame. '
767 'When off, frame 0 is used as rest transformations for objects'
769 default=False
772 export_rest_position_armature: BoolProperty(
773 name='Use Rest Position Armature',
774 description=(
775 "Export armatures using rest position as joints' rest pose. "
776 "When off, current frame pose is used as rest pose"
778 default=True
781 export_anim_scene_split_object: BoolProperty(
782 name='Split Animation by Object',
783 description=(
784 "Export Scene as seen in Viewport, "
785 "But split animation by Object"
787 default=True
790 export_skins: BoolProperty(
791 name='Skinning',
792 description='Export skinning (armature) data',
793 default=True
796 export_influence_nb: IntProperty(
797 name='Bone Influences',
798 description='Choose how many Bone influences to export',
799 default=4,
800 min=1
803 export_all_influences: BoolProperty(
804 name='Include All Bone Influences',
805 description='Allow export of all joint vertex influences. Models may appear incorrectly in many viewers',
806 default=False
809 export_morph: BoolProperty(
810 name='Shape Keys',
811 description='Export shape keys (morph targets)',
812 default=True
815 export_morph_normal: BoolProperty(
816 name='Shape Key Normals',
817 description='Export vertex normals with shape keys (morph targets)',
818 default=True
821 export_morph_tangent: BoolProperty(
822 name='Shape Key Tangents',
823 description='Export vertex tangents with shape keys (morph targets)',
824 default=False
827 export_morph_animation: BoolProperty(
828 name='Shape Key Animations',
829 description='Export shape keys animations (morph targets)',
830 default=True
833 export_morph_reset_sk_data: BoolProperty(
834 name='Reset shape keys between actions',
835 description=(
836 "Reset shape keys between each action exported. "
837 "This is needed when some SK channels are not keyed on some animations"
839 default=True
842 export_lights: BoolProperty(
843 name='Punctual Lights',
844 description='Export directional, point, and spot lights. '
845 'Uses "KHR_lights_punctual" glTF extension',
846 default=False
849 export_try_sparse_sk: BoolProperty(
850 name='Use Sparse Accessor if better',
851 description='Try using Sparse Accessor if it saves space',
852 default=True
855 export_try_omit_sparse_sk: BoolProperty(
856 name='Omitting Sparse Accessor if data is empty',
857 description='Omitting Sparse Accessor if data is empty',
858 default=False
861 export_gpu_instances: BoolProperty(
862 name='GPU Instances',
863 description='Export using EXT_mesh_gpu_instancing. '
864 'Limited to children of a given Empty. '
865 'Multiple materials might be omitted',
866 default=False
869 export_action_filter: BoolProperty(
870 name='Filter Actions',
871 description='Filter Actions to be exported',
872 default=False,
873 update=on_export_action_filter_changed,
876 export_convert_animation_pointer: BoolProperty(
877 name='Convert TRS/weights to Animation Pointer',
878 description='Export TRS and weights as Animation Pointer. '
879 'Using KHR_animation_pointer extension',
880 default=False
883 # This parameter is only here for backward compatibility, as this option is removed in 3.6
884 # This option does nothing, and is not displayed in UI
885 # What you are looking for is probably "export_animation_mode"
886 export_nla_strips: BoolProperty(
887 name='Group by NLA Track',
888 description=(
889 "When on, multiple actions become part of the same glTF animation if "
890 "they're pushed onto NLA tracks with the same name. "
891 "When off, all the currently assigned actions become one glTF animation"
893 default=True
896 # Keep for back compatibility, but no more used
897 export_original_specular: BoolProperty(
898 name='Export original PBR Specular',
899 description=(
900 'Export original glTF PBR Specular, instead of Blender Principled Shader Specular'
902 default=False,
905 will_save_settings: BoolProperty(
906 name='Remember Export Settings',
907 description='Store glTF export settings in the Blender project',
908 default=False)
910 export_hierarchy_full_collections: BoolProperty(
911 name='Full Collection Hierarchy',
912 description='Export full hierarchy, including intermediate collections',
913 default=False
916 export_extra_animations: BoolProperty(
917 name='Prepare extra animations',
918 description=(
919 'Export additional animations'
920 'This feature is not standard and needs an external extension to be included in the glTF file'
922 default=False
925 # Custom scene property for saving settings
926 scene_key = "glTF2ExportSettings"
930 def check(self, _context):
931 # Ensure file extension matches format
932 old_filepath = self.filepath
933 self.filepath = ensure_filepath_matches_export_format(
934 self.filepath,
935 self.export_format,
937 return self.filepath != old_filepath
939 def invoke(self, context, event):
940 settings = context.scene.get(self.scene_key)
941 self.will_save_settings = False
942 if settings:
943 try:
944 for (k, v) in settings.items():
945 setattr(self, k, v)
946 self.will_save_settings = True
948 # Update filter if user saved settings
949 if hasattr(self, 'export_format'):
950 self.filter_glob = '*.glb' if self.export_format == 'GLB' else '*.gltf'
952 except (AttributeError, TypeError):
953 self.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings")
954 del context.scene[self.scene_key]
956 import sys
957 preferences = bpy.context.preferences
958 for addon_name in preferences.addons.keys():
959 try:
960 if hasattr(sys.modules[addon_name], 'glTF2ExportUserExtension') or hasattr(sys.modules[addon_name], 'glTF2ExportUserExtensions'):
961 exporter_extension_panel_unregister_functors.append(sys.modules[addon_name].register_panel())
962 except Exception:
963 pass
965 self.has_active_exporter_extensions = len(exporter_extension_panel_unregister_functors) > 0
966 return ExportHelper.invoke(self, context, event)
968 def save_settings(self, context):
969 # find all props to save
970 exceptional = [
971 # options that don't start with 'export_'
972 'use_selection',
973 'use_visible',
974 'use_renderable',
975 'use_active_collection_with_nested',
976 'use_active_collection',
977 'use_mesh_edges',
978 'use_mesh_vertices',
979 'use_active_scene',
980 'collection',
982 all_props = self.properties
983 export_props = {
984 x: getattr(self, x) for x in dir(all_props)
985 if (x.startswith("export_") or x in exceptional) and all_props.get(x) is not None
987 context.scene[self.scene_key] = export_props
989 def execute(self, context):
990 import os
991 import datetime
992 import logging
993 from .io.com.gltf2_io_debug import Log
994 from .blender.exp import gltf2_blender_export
995 from .io.com.gltf2_io_path import path_to_uri
997 if self.will_save_settings:
998 self.save_settings(context)
1000 self.check(context) # ensure filepath has the right extension
1002 # All custom export settings are stored in this container.
1003 export_settings = {}
1005 export_settings['loglevel'] = logging.INFO
1007 export_settings['exported_images'] = {}
1008 export_settings['exported_texture_nodes'] = []
1009 export_settings['additional_texture_export'] = []
1010 export_settings['additional_texture_export_current_idx'] = 0
1012 export_settings['timestamp'] = datetime.datetime.now()
1013 export_settings['gltf_export_id'] = self.gltf_export_id
1014 export_settings['gltf_filepath'] = self.filepath
1015 export_settings['gltf_filedirectory'] = os.path.dirname(export_settings['gltf_filepath']) + '/'
1016 export_settings['gltf_texturedirectory'] = os.path.join(
1017 export_settings['gltf_filedirectory'],
1018 self.export_texture_dir,
1020 export_settings['gltf_keep_original_textures'] = self.export_keep_originals
1022 export_settings['gltf_format'] = self.export_format
1023 export_settings['gltf_image_format'] = self.export_image_format
1024 export_settings['gltf_add_webp'] = self.export_image_add_webp
1025 export_settings['gltf_webp_fallback'] = self.export_image_webp_fallback
1026 export_settings['gltf_image_quality'] = self.export_image_quality
1027 export_settings['gltf_copyright'] = self.export_copyright
1028 export_settings['gltf_texcoords'] = self.export_texcoords
1029 export_settings['gltf_normals'] = self.export_normals
1030 export_settings['gltf_tangents'] = self.export_tangents and self.export_normals
1031 export_settings['gltf_loose_edges'] = self.use_mesh_edges
1032 export_settings['gltf_loose_points'] = self.use_mesh_vertices
1034 if is_draco_available():
1035 export_settings['gltf_draco_mesh_compression'] = self.export_draco_mesh_compression_enable
1036 export_settings['gltf_draco_mesh_compression_level'] = self.export_draco_mesh_compression_level
1037 export_settings['gltf_draco_position_quantization'] = self.export_draco_position_quantization
1038 export_settings['gltf_draco_normal_quantization'] = self.export_draco_normal_quantization
1039 export_settings['gltf_draco_texcoord_quantization'] = self.export_draco_texcoord_quantization
1040 export_settings['gltf_draco_color_quantization'] = self.export_draco_color_quantization
1041 export_settings['gltf_draco_generic_quantization'] = self.export_draco_generic_quantization
1042 else:
1043 export_settings['gltf_draco_mesh_compression'] = False
1045 export_settings['gltf_gn_mesh'] = self.export_gn_mesh
1047 export_settings['gltf_materials'] = self.export_materials
1048 export_settings['gltf_attributes'] = self.export_attributes
1049 export_settings['gltf_cameras'] = self.export_cameras
1051 export_settings['gltf_unused_textures'] = self.export_unused_textures
1052 export_settings['gltf_unused_images'] = self.export_unused_images
1054 export_settings['gltf_visible'] = self.use_visible
1055 export_settings['gltf_renderable'] = self.use_renderable
1057 export_settings['gltf_active_collection'] = self.use_active_collection
1058 if self.use_active_collection:
1059 export_settings['gltf_active_collection_with_nested'] = self.use_active_collection_with_nested
1060 else:
1061 export_settings['gltf_active_collection_with_nested'] = False
1062 export_settings['gltf_active_scene'] = self.use_active_scene
1063 export_settings['gltf_collection'] = self.collection
1065 export_settings['gltf_selected'] = self.use_selection
1066 export_settings['gltf_layers'] = True # self.export_layers
1067 export_settings['gltf_extras'] = self.export_extras
1068 export_settings['gltf_yup'] = self.export_yup
1069 export_settings['gltf_apply'] = self.export_apply
1070 export_settings['gltf_shared_accessors'] = self.export_shared_accessors
1071 export_settings['gltf_current_frame'] = self.export_current_frame
1072 export_settings['gltf_animations'] = self.export_animations
1073 export_settings['gltf_def_bones'] = self.export_def_bones
1074 export_settings['gltf_flatten_bones_hierarchy'] = self.export_hierarchy_flatten_bones
1075 export_settings['gltf_flatten_obj_hierarchy'] = self.export_hierarchy_flatten_objs
1076 export_settings['gltf_armature_object_remove'] = self.export_armature_object_remove
1077 export_settings['gltf_leaf_bone'] = self.export_leaf_bone
1078 if self.export_animations:
1079 export_settings['gltf_frame_range'] = self.export_frame_range
1080 export_settings['gltf_force_sampling'] = self.export_force_sampling
1081 if not self.export_force_sampling:
1082 export_settings['gltf_def_bones'] = False
1083 export_settings['gltf_bake_animation'] = False
1084 export_settings['gltf_animation_mode'] = self.export_animation_mode
1085 if export_settings['gltf_animation_mode'] == "NLA_TRACKS":
1086 export_settings['gltf_force_sampling'] = True
1087 if export_settings['gltf_animation_mode'] == "SCENE":
1088 export_settings['gltf_anim_scene_split_object'] = self.export_anim_scene_split_object
1089 else:
1090 export_settings['gltf_anim_scene_split_object'] = False
1092 if export_settings['gltf_animation_mode'] in ['NLA_TRACKS', 'SCENE']:
1093 export_settings['gltf_export_anim_pointer'] = self.export_pointer_animation
1094 if self.export_pointer_animation:
1095 export_settings['gltf_trs_w_animation_pointer'] = self.export_convert_animation_pointer
1096 else:
1097 export_settings['gltf_trs_w_animation_pointer'] = False
1098 else:
1099 export_settings['gltf_trs_w_animation_pointer'] = False
1100 export_settings['gltf_export_anim_pointer'] = False
1102 export_settings['gltf_nla_strips_merged_animation_name'] = self.export_nla_strips_merged_animation_name
1103 export_settings['gltf_optimize_animation'] = self.export_optimize_animation_size
1104 export_settings['gltf_optimize_animation_keep_armature'] = self.export_optimize_animation_keep_anim_armature
1105 export_settings['gltf_optimize_animation_keep_object'] = self.export_optimize_animation_keep_anim_object
1106 export_settings['gltf_optimize_armature_disable_viewport'] = self.export_optimize_armature_disable_viewport
1107 export_settings['gltf_export_anim_single_armature'] = self.export_anim_single_armature
1108 export_settings['gltf_export_reset_pose_bones'] = self.export_reset_pose_bones
1109 export_settings['gltf_export_reset_sk_data'] = self.export_morph_reset_sk_data
1110 export_settings['gltf_bake_animation'] = self.export_bake_animation
1111 export_settings['gltf_negative_frames'] = self.export_negative_frame
1112 export_settings['gltf_anim_slide_to_zero'] = self.export_anim_slide_to_zero
1113 export_settings['gltf_export_extra_animations'] = self.export_extra_animations
1114 else:
1115 export_settings['gltf_frame_range'] = False
1116 export_settings['gltf_force_sampling'] = False
1117 export_settings['gltf_bake_animation'] = False
1118 export_settings['gltf_optimize_animation'] = False
1119 export_settings['gltf_optimize_animation_keep_armature'] = False
1120 export_settings['gltf_optimize_animation_keep_object'] = False
1121 export_settings['gltf_optimize_armature_disable_viewport'] = False
1122 export_settings['gltf_export_anim_single_armature'] = False
1123 export_settings['gltf_export_reset_pose_bones'] = False
1124 export_settings['gltf_export_reset_sk_data'] = False
1125 export_settings['gltf_export_extra_animations'] = False
1126 export_settings['gltf_skins'] = self.export_skins
1127 if self.export_skins:
1128 export_settings['gltf_all_vertex_influences'] = self.export_all_influences
1129 export_settings['gltf_vertex_influences_nb'] = self.export_influence_nb
1130 else:
1131 export_settings['gltf_all_vertex_influences'] = False
1132 export_settings['gltf_def_bones'] = False
1133 export_settings['gltf_rest_position_armature'] = self.export_rest_position_armature
1134 export_settings['gltf_frame_step'] = self.export_frame_step
1136 export_settings['gltf_morph'] = self.export_morph
1137 if self.export_morph:
1138 export_settings['gltf_morph_normal'] = self.export_morph_normal
1139 export_settings['gltf_morph_tangent'] = self.export_morph_tangent
1140 export_settings['gltf_morph_anim'] = self.export_morph_animation
1141 else:
1142 export_settings['gltf_morph_normal'] = False
1143 export_settings['gltf_morph_tangent'] = False
1144 export_settings['gltf_morph_anim'] = False
1146 export_settings['gltf_lights'] = self.export_lights
1147 export_settings['gltf_lighting_mode'] = self.export_import_convert_lighting_mode
1149 export_settings['gltf_gpu_instances'] = self.export_gpu_instances
1151 export_settings['gltf_try_sparse_sk'] = self.export_try_sparse_sk
1152 export_settings['gltf_try_omit_sparse_sk'] = self.export_try_omit_sparse_sk
1153 if not self.export_try_sparse_sk:
1154 export_settings['gltf_try_omit_sparse_sk'] = False
1156 export_settings['gltf_hierarchy_full_collections'] = self.export_hierarchy_full_collections
1158 # gltfpack stuff
1159 export_settings['gltf_use_gltfpack'] = self.export_use_gltfpack
1160 if self.export_use_gltfpack:
1161 export_settings['gltf_gltfpack_tc'] = self.export_gltfpack_tc
1162 export_settings['gltf_gltfpack_tq'] = self.export_gltfpack_tq
1164 export_settings['gltf_gltfpack_si'] = self.export_gltfpack_si
1165 export_settings['gltf_gltfpack_sa'] = self.export_gltfpack_sa
1166 export_settings['gltf_gltfpack_slb'] = self.export_gltfpack_slb
1168 export_settings['gltf_gltfpack_vp'] = self.export_gltfpack_vp
1169 export_settings['gltf_gltfpack_vt'] = self.export_gltfpack_vt
1170 export_settings['gltf_gltfpack_vn'] = self.export_gltfpack_vn
1171 export_settings['gltf_gltfpack_vc'] = self.export_gltfpack_vc
1173 export_settings['gltf_gltfpack_vpi'] = self.export_gltfpack_vpi
1175 export_settings['gltf_gltfpack_noq'] = self.export_gltfpack_noq
1177 export_settings['gltf_binary'] = bytearray()
1178 export_settings['gltf_binaryfilename'] = (
1179 path_to_uri(os.path.splitext(os.path.basename(self.filepath))[0] + '.bin')
1182 user_extensions = []
1183 pre_export_callbacks = []
1184 post_export_callbacks = []
1186 import sys
1187 preferences = bpy.context.preferences
1188 for addon_name in preferences.addons.keys():
1189 try:
1190 module = sys.modules[addon_name]
1191 except Exception:
1192 continue
1193 if hasattr(module, 'glTF2ExportUserExtension'):
1194 extension_ctor = module.glTF2ExportUserExtension
1195 user_extensions.append(extension_ctor())
1196 if hasattr(module, 'glTF2ExportUserExtensions'):
1197 extension_ctors = module.glTF2ExportUserExtensions
1198 for extension_ctor in extension_ctors:
1199 user_extensions.append(extension_ctor())
1200 if hasattr(module, 'glTF2_pre_export_callback'):
1201 pre_export_callbacks.append(module.glTF2_pre_export_callback)
1202 if hasattr(module, 'glTF2_post_export_callback'):
1203 post_export_callbacks.append(module.glTF2_post_export_callback)
1204 export_settings['gltf_user_extensions'] = user_extensions
1205 export_settings['pre_export_callbacks'] = pre_export_callbacks
1206 export_settings['post_export_callbacks'] = post_export_callbacks
1209 # Initialize logging for export
1210 export_settings['log'] = Log(export_settings['loglevel'])
1213 profile = bpy.app.debug_value == 102
1214 if profile:
1215 import cProfile, pstats, io
1216 from pstats import SortKey
1217 pr = cProfile.Profile()
1218 pr.enable()
1219 res = gltf2_blender_export.save(context, export_settings)
1220 pr.disable()
1221 s = io.StringIO()
1222 sortby = SortKey.TIME
1223 ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
1224 ps.print_stats()
1225 print(s.getvalue())
1226 else:
1227 res = gltf2_blender_export.save(context, export_settings)
1229 # Display popup log, if any
1230 for message_type, message in export_settings['log'].messages():
1231 self.report({message_type}, message)
1233 export_settings['log'].flush()
1235 return res
1237 def draw(self, context):
1238 operator = self
1239 layout = self.layout
1240 layout.use_property_split = True
1241 layout.use_property_decorate = False # No animation.
1243 # Are we inside the File browser
1244 is_file_browser = context.space_data.type == 'FILE_BROWSER'
1246 export_main(layout, operator, is_file_browser)
1247 export_panel_include(layout, operator, is_file_browser)
1248 export_panel_transform(layout, operator)
1249 export_panel_data(layout, operator)
1250 export_panel_animation(layout, operator)
1252 # If gltfpack is not setup in plugin preferences -> don't show any gltfpack relevant options in export dialog
1253 gltfpack_path = context.preferences.addons['io_scene_gltf2'].preferences.gltfpack_path_ui.strip()
1254 if gltfpack_path != '':
1255 export_panel_gltfpack(layout, operator)
1257 def export_main(layout, operator, is_file_browser):
1258 layout.prop(operator, 'export_format')
1259 if operator.export_format == 'GLTF_SEPARATE':
1260 layout.prop(operator, 'export_keep_originals')
1261 if operator.export_keep_originals is False:
1262 layout.prop(operator, 'export_texture_dir', icon='FILE_FOLDER')
1263 if operator.export_format == 'GLTF_EMBEDDED':
1264 layout.label(text="This is the least efficient of the available forms, and should only be used when required.", icon='ERROR')
1266 layout.prop(operator, 'export_copyright')
1267 if is_file_browser:
1268 layout.prop(operator, 'will_save_settings')
1271 def export_panel_include(layout, operator, is_file_browser):
1272 header, body = layout.panel("GLTF_export_include", default_closed=True)
1273 header.label(text="Include")
1274 if body:
1275 if is_file_browser:
1276 col = body.column(heading = "Limit to", align = True)
1277 col.prop(operator, 'use_selection')
1278 col.prop(operator, 'use_visible')
1279 col.prop(operator, 'use_renderable')
1280 col.prop(operator, 'use_active_collection')
1281 if operator.use_active_collection:
1282 col.prop(operator, 'use_active_collection_with_nested')
1283 col.prop(operator, 'use_active_scene')
1285 col = body.column(heading = "Data", align = True)
1286 col.prop(operator, 'export_extras')
1287 col.prop(operator, 'export_cameras')
1288 col.prop(operator, 'export_lights')
1291 def export_panel_transform(layout, operator):
1292 header, body = layout.panel("GLTF_export_transform", default_closed=True)
1293 header.label(text="Transform")
1294 if body:
1295 body.prop(operator, 'export_yup')
1298 def export_panel_data(layout, operator):
1299 header, body = layout.panel("GLTF_export_data", default_closed=True)
1300 header.label(text="Data")
1301 if body:
1302 export_panel_data_scene_graph(body, operator)
1303 export_panel_data_mesh(body, operator)
1304 export_panel_data_material(body, operator)
1305 export_panel_data_shapekeys(body, operator)
1306 export_panel_data_armature(body, operator)
1307 export_panel_data_skinning(body, operator)
1308 export_panel_data_lighting(body, operator)
1310 if is_draco_available():
1311 export_panel_data_compression(body, operator)
1314 def export_panel_data_scene_graph(layout, operator):
1315 header, body = layout.panel("GLTF_export_data_scene_graph", default_closed=True)
1316 header.label(text="Scene Graph")
1317 if body:
1318 body.prop(operator, 'export_gn_mesh')
1319 body.prop(operator, 'export_gpu_instances')
1320 body.prop(operator, 'export_hierarchy_flatten_objs')
1321 body.prop(operator, 'export_hierarchy_full_collections')
1324 def export_panel_data_mesh(layout, operator):
1325 header, body = layout.panel("GLTF_export_data_mesh", default_closed=True)
1326 header.label(text="Mesh")
1327 if body:
1328 body.prop(operator, 'export_apply')
1329 body.prop(operator, 'export_texcoords')
1330 body.prop(operator, 'export_normals')
1331 col = body.column()
1332 col.active = operator.export_normals
1333 col.prop(operator, 'export_tangents')
1334 body.prop(operator, 'export_attributes')
1336 col = body.column()
1337 col.prop(operator, 'use_mesh_edges')
1338 col.prop(operator, 'use_mesh_vertices')
1340 col = body.column()
1341 col.prop(operator, 'export_shared_accessors')
1344 def export_panel_data_material(layout, operator):
1345 header, body = layout.panel("GLTF_export_data_material", default_closed=True)
1346 header.label(text="Material")
1347 if body:
1348 body.prop(operator, 'export_materials')
1349 col = body.column()
1350 col.active = operator.export_materials == "EXPORT"
1351 col.prop(operator, 'export_image_format')
1352 if operator.export_image_format in ["AUTO", "JPEG", "WEBP"]:
1353 col.prop(operator, 'export_image_quality')
1354 col = body.column()
1355 col.active = operator.export_image_format != "WEBP"
1356 col.prop(operator, "export_image_add_webp")
1357 col = body.column()
1358 col.active = operator.export_image_format != "WEBP"
1359 col.prop(operator, "export_image_webp_fallback")
1361 header, sub_body = body.panel("GLTF_export_data_material_unused", default_closed=True)
1362 header.label(text="Unused Textures & Images")
1363 if sub_body:
1364 row = sub_body.row()
1365 row.prop(operator, 'export_unused_images')
1366 row = sub_body.row()
1367 row.prop(operator, 'export_unused_textures')
1370 def export_panel_data_shapekeys(layout, operator):
1371 header, body = layout.panel("GLTF_export_data_shapekeys", default_closed=True)
1372 header.use_property_split = False
1373 header.prop(operator, "export_morph", text="")
1374 header.label(text="Shape Keys")
1375 if body:
1376 body.active = operator.export_morph
1378 body.prop(operator, 'export_morph_normal')
1379 col = body.column()
1380 col.active = operator.export_morph_normal
1381 col.prop(operator, 'export_morph_tangent')
1383 # Data-Shape Keys-Optimize
1384 header, sub_body = body.panel("GLTF_export_data_shapekeys_optimize", default_closed=True)
1385 header.label(text="Optimize Shape Keys")
1386 if sub_body:
1387 row = sub_body.row()
1388 row.prop(operator, 'export_try_sparse_sk')
1390 row = sub_body.row()
1391 row.active = operator.export_try_sparse_sk
1392 row.prop(operator, 'export_try_omit_sparse_sk')
1395 def export_panel_data_armature(layout, operator):
1396 header, body = layout.panel("GLTF_export_data_armature", default_closed=True)
1397 header.label(text="Armature")
1398 if body:
1399 body.active = operator.export_skins
1401 body.prop(operator, 'export_rest_position_armature')
1403 row = body.row()
1404 row.active = operator.export_force_sampling
1405 row.prop(operator, 'export_def_bones')
1406 if operator.export_force_sampling is False and operator.export_def_bones is True:
1407 body.label(text="Export only deformation bones is not possible when not sampling animation")
1408 row = body.row()
1409 row.prop(operator, 'export_armature_object_remove')
1410 row = body.row()
1411 row.prop(operator, 'export_hierarchy_flatten_bones')
1414 def export_panel_data_skinning(layout, operator):
1415 header, body = layout.panel("GLTF_export_data_skinning", default_closed=True)
1416 header.use_property_split = False
1417 header.prop(operator, "export_skins", text="")
1418 header.label(text="Skinning")
1419 if body:
1420 body.active = operator.export_skins
1422 row = body.row()
1423 row.prop(operator, 'export_influence_nb')
1424 row.active = not operator.export_all_influences
1425 body.prop(operator, 'export_all_influences')
1428 def export_panel_data_lighting(layout, operator):
1429 header, body = layout.panel("GLTF_export_data_lighting", default_closed=True)
1430 header.label(text="Lighting")
1431 if body:
1432 body.prop(operator, 'export_import_convert_lighting_mode')
1435 def export_panel_data_compression(layout, operator):
1436 header, body = layout.panel("GLTF_export_data_compression", default_closed=True)
1437 header.use_property_split = False
1438 header.prop(operator, "export_draco_mesh_compression_enable", text="")
1439 header.label(text="Compression")
1440 if body:
1441 body.active = operator.export_draco_mesh_compression_enable
1443 body.prop(operator, 'export_draco_mesh_compression_level')
1445 col = body.column(align=True)
1446 col.prop(operator, 'export_draco_position_quantization', text="Quantize Position")
1447 col.prop(operator, 'export_draco_normal_quantization', text="Normal")
1448 col.prop(operator, 'export_draco_texcoord_quantization', text="Tex Coord")
1449 col.prop(operator, 'export_draco_color_quantization', text="Color")
1450 col.prop(operator, 'export_draco_generic_quantization', text="Generic")
1453 def export_panel_animation(layout, operator):
1454 header, body = layout.panel("GLTF_export_animation", default_closed=True)
1455 header.use_property_split = False
1456 header.prop(operator, "export_animations", text="")
1457 header.label(text="Animation")
1458 if body:
1459 body.active = operator.export_animations
1461 body.prop(operator, 'export_animation_mode')
1462 if operator.export_animation_mode == "ACTIVE_ACTIONS":
1463 layout.prop(operator, 'export_nla_strips_merged_animation_name')
1465 row = body.row()
1466 row.active = operator.export_force_sampling and operator.export_animation_mode in ['ACTIONS', 'ACTIVE_ACTIONS', 'BROACAST']
1467 row.prop(operator, 'export_bake_animation')
1468 if operator.export_animation_mode == "SCENE":
1469 body.prop(operator, 'export_anim_scene_split_object')
1470 row = body.row()
1472 if operator.export_animation_mode in ["NLA_TRACKS", "SCENE"]:
1473 export_panel_animation_notes(body, operator)
1474 export_panel_animation_ranges(body, operator)
1475 export_panel_animation_armature(body, operator)
1476 export_panel_animation_shapekeys(body, operator)
1477 export_panel_animation_sampling(body, operator)
1478 export_panel_animation_pointer(body, operator)
1479 export_panel_animation_optimize(body, operator)
1480 if operator.export_animation_mode in ['ACTIONS', 'ACTIVE_ACTIONS']:
1481 export_panel_animation_extra(body, operator)
1483 from .blender.com.gltf2_blender_ui import export_panel_animation_action_filter
1484 export_panel_animation_action_filter(body, operator)
1487 def export_panel_animation_notes(layout, operator):
1488 header, body = layout.panel("GLTF_export_animation_notes", default_closed=True)
1489 header.label(text="Notes")
1490 if body:
1491 if operator.export_animation_mode == "SCENE":
1492 body.label(text="Scene mode uses full bake mode:")
1493 body.label(text="- sampling is active")
1494 body.label(text="- baking all objects is active")
1495 body.label(text="- Using scene frame range")
1496 elif operator.export_animation_mode == "NLA_TRACKS":
1497 body.label(text="Track mode uses full bake mode:")
1498 body.label(text="- sampling is active")
1499 body.label(text="- baking all objects is active")
1502 def export_panel_animation_ranges(layout, operator):
1503 header, body = layout.panel("GLTF_export_animation_ranges", default_closed=True)
1504 header.label(text="Rest & Ranges")
1505 if body:
1506 body.active = operator.export_animations
1508 body.prop(operator, 'export_current_frame')
1509 row = body.row()
1510 row.active = operator.export_animation_mode in ['ACTIONS', 'ACTIVE_ACTIONS', 'BROADCAST', 'NLA_TRACKS']
1511 row.prop(operator, 'export_frame_range')
1512 body.prop(operator, 'export_anim_slide_to_zero')
1513 row = body.row()
1514 row.active = operator.export_animation_mode in ['ACTIONS', 'ACTIVE_ACTIONS', 'BROADCAST', 'NLA_TRACKS']
1515 body.prop(operator, 'export_negative_frame')
1518 def export_panel_animation_armature(layout, operator):
1519 header, body = layout.panel("GLTF_export_animation_armature", default_closed=True)
1520 header.label(text="Armature")
1521 if body:
1522 body.active = operator.export_animations
1524 body.prop(operator, 'export_anim_single_armature')
1525 body.prop(operator, 'export_reset_pose_bones')
1528 def export_panel_animation_shapekeys(layout, operator):
1529 header, body = layout.panel("GLTF_export_animation_shapekeys", default_closed=True)
1530 header.use_property_split = False
1531 header.prop(operator, "export_morph_animation", text="")
1532 header.label(text="Shape Keys Animation")
1533 if body:
1534 body.active = operator.export_animations
1536 body.prop(operator, 'export_morph_reset_sk_data')
1539 def export_panel_animation_sampling(layout, operator):
1540 header, body = layout.panel("GLTF_export_animation_sampling", default_closed=True)
1541 header.use_property_split = False
1542 header.prop(operator, "export_force_sampling", text="")
1543 header.label(text="Sampling Animations")
1544 if body:
1545 body.active = operator.export_animations
1547 body.prop(operator, 'export_frame_step')
1549 def export_panel_animation_pointer(layout, operator):
1550 header, body = layout.panel("GLTF_export_animation_pointer", default_closed=True)
1551 header.use_property_split = False
1552 header.active = operator.export_animations and operator.export_animation_mode in ['NLA_TRACKS', 'SCENE']
1553 header.prop(operator, "export_pointer_animation", text="")
1554 header.label(text="Animation Pointer (Experimental)")
1555 if body:
1558 row = body.row()
1559 row.active = operator.export_pointer_animation
1560 row.prop(operator, 'export_convert_animation_pointer')
1562 def export_panel_animation_optimize(layout, operator):
1563 header, body = layout.panel("GLTF_export_animation_optimize", default_closed=True)
1564 header.label(text="Optimize Animations")
1565 if body:
1566 body.active = operator.export_animations
1568 body.prop(operator, 'export_optimize_animation_size')
1570 row = body.row()
1571 row.prop(operator, 'export_optimize_animation_keep_anim_armature')
1573 row = body.row()
1574 row.prop(operator, 'export_optimize_animation_keep_anim_object')
1576 row = body.row()
1577 row.prop(operator, 'export_optimize_armature_disable_viewport')
1580 def export_panel_animation_extra(layout, operator):
1581 header, body = layout.panel("GLTF_export_animation_extra", default_closed=True)
1582 header.label(text="Extra Animations")
1583 if body:
1584 body.active = operator.export_animations
1586 body.prop(operator, 'export_extra_animations')
1589 def export_panel_gltfpack(layout, operator):
1590 header, body = layout.panel("GLTF_export_gltfpack", default_closed=True)
1591 header.label(text="gltfpack")
1592 if body:
1593 col = body.column(heading = "gltfpack", align = True)
1594 col.prop(operator, 'export_use_gltfpack')
1596 col = body.column(heading = "Textures", align = True)
1597 col.prop(operator, 'export_gltfpack_tc')
1598 col.prop(operator, 'export_gltfpack_tq')
1599 col = body.column(heading = "Simplification", align = True)
1600 col.prop(operator, 'export_gltfpack_si')
1601 col.prop(operator, 'export_gltfpack_sa')
1602 col.prop(operator, 'export_gltfpack_slb')
1603 col = body.column(heading = "Vertices", align = True)
1604 col.prop(operator, 'export_gltfpack_vp')
1605 col.prop(operator, 'export_gltfpack_vt')
1606 col.prop(operator, 'export_gltfpack_vn')
1607 col.prop(operator, 'export_gltfpack_vc')
1608 col = body.column(heading = "Vertex positions", align = True)
1609 col.prop(operator, 'export_gltfpack_vpi')
1610 #col = body.column(heading = "Animations", align = True)
1611 #col = body.column(heading = "Scene", align = True)
1612 col = body.column(heading = "Miscellaneous", align = True)
1613 col.prop(operator, 'export_gltfpack_noq')
1616 class ExportGLTF2(bpy.types.Operator, ExportGLTF2_Base, ExportHelper):
1617 """Export scene as glTF 2.0 file"""
1618 bl_idname = 'export_scene.gltf'
1619 bl_label = 'Export glTF 2.0'
1621 filename_ext = ''
1623 filter_glob: StringProperty(default='*.glb', options={'HIDDEN'})
1626 def menu_func_export(self, context):
1627 self.layout.operator(ExportGLTF2.bl_idname, text='glTF 2.0 (.glb/.gltf)')
1630 class ImportGLTF2(Operator, ConvertGLTF2_Base, ImportHelper):
1631 """Load a glTF 2.0 file"""
1632 bl_idname = 'import_scene.gltf'
1633 bl_label = 'Import glTF 2.0'
1634 bl_options = {'REGISTER', 'UNDO'}
1636 filter_glob: StringProperty(default="*.glb;*.gltf", options={'HIDDEN'})
1638 files: CollectionProperty(
1639 name="File Path",
1640 type=bpy.types.OperatorFileListElement,
1643 loglevel: IntProperty(
1644 name='Log Level',
1645 description="Log Level")
1647 import_pack_images: BoolProperty(
1648 name='Pack Images',
1649 description='Pack all images into .blend file',
1650 default=True
1653 merge_vertices: BoolProperty(
1654 name='Merge Vertices',
1655 description=(
1656 'The glTF format requires discontinuous normals, UVs, and '
1657 'other vertex attributes to be stored as separate vertices, '
1658 'as required for rendering on typical graphics hardware. '
1659 'This option attempts to combine co-located vertices where possible. '
1660 'Currently cannot combine verts with different normals'
1662 default=False,
1665 import_shading: EnumProperty(
1666 name="Shading",
1667 items=(("NORMALS", "Use Normal Data", ""),
1668 ("FLAT", "Flat Shading", ""),
1669 ("SMOOTH", "Smooth Shading", "")),
1670 description="How normals are computed during import",
1671 default="NORMALS")
1673 bone_heuristic: EnumProperty(
1674 name="Bone Dir",
1675 items=(
1676 ("BLENDER", "Blender (best for import/export round trip)",
1677 "Good for re-importing glTFs exported from Blender, "
1678 "and re-exporting glTFs to glTFs after Blender editing. "
1679 "Bone tips are placed on their local +Y axis (in glTF space)"),
1680 ("TEMPERANCE", "Temperance (average)",
1681 "Decent all-around strategy. "
1682 "A bone with one child has its tip placed on the local axis "
1683 "closest to its child"),
1684 ("FORTUNE", "Fortune (may look better, less accurate)",
1685 "Might look better than Temperance, but also might have errors. "
1686 "A bone with one child has its tip placed at its child's root. "
1687 "Non-uniform scalings may get messed up though, so beware"),
1689 description="Heuristic for placing bones. Tries to make bones pretty",
1690 default="BLENDER",
1693 guess_original_bind_pose: BoolProperty(
1694 name='Guess Original Bind Pose',
1695 description=(
1696 'Try to guess the original bind pose for skinned meshes from '
1697 'the inverse bind matrices. '
1698 'When off, use default/rest pose as bind pose'
1700 default=True,
1703 import_webp_texture: BoolProperty(
1704 name='Import WebP textures',
1705 description=(
1706 "If a texture exists in WebP format, "
1707 "loads the WebP texture instead of the fallback PNG/JPEG one"
1709 default=False,
1712 def draw(self, context):
1713 layout = self.layout
1715 layout.use_property_split = True
1716 layout.use_property_decorate = False # No animation.
1718 layout.prop(self, 'import_pack_images')
1719 layout.prop(self, 'merge_vertices')
1720 layout.prop(self, 'import_shading')
1721 layout.prop(self, 'guess_original_bind_pose')
1722 layout.prop(self, 'bone_heuristic')
1723 layout.prop(self, 'export_import_convert_lighting_mode')
1724 layout.prop(self, 'import_webp_texture')
1726 def invoke(self, context, event):
1727 import sys
1728 preferences = bpy.context.preferences
1729 for addon_name in preferences.addons.keys():
1730 try:
1731 if hasattr(sys.modules[addon_name], 'glTF2ImportUserExtension') or hasattr(sys.modules[addon_name], 'glTF2ImportUserExtensions'):
1732 importer_extension_panel_unregister_functors.append(sys.modules[addon_name].register_panel())
1733 except Exception:
1734 pass
1736 self.has_active_importer_extensions = len(importer_extension_panel_unregister_functors) > 0
1737 return ImportHelper.invoke_popup(self, context)
1739 def execute(self, context):
1740 return self.import_gltf2(context)
1742 def import_gltf2(self, context):
1743 import os
1745 self.set_debug_log()
1746 import_settings = self.as_keywords()
1748 user_extensions = []
1750 import sys
1751 preferences = bpy.context.preferences
1752 for addon_name in preferences.addons.keys():
1753 try:
1754 module = sys.modules[addon_name]
1755 except Exception:
1756 continue
1757 if hasattr(module, 'glTF2ImportUserExtension'):
1758 extension_ctor = module.glTF2ImportUserExtension
1759 user_extensions.append(extension_ctor())
1760 import_settings['import_user_extensions'] = user_extensions
1762 if self.files:
1763 # Multiple file import
1764 ret = {'CANCELLED'}
1765 dirname = os.path.dirname(self.filepath)
1766 for file in self.files:
1767 path = os.path.join(dirname, file.name)
1768 if self.unit_import(path, import_settings) == {'FINISHED'}:
1769 ret = {'FINISHED'}
1770 return ret
1771 else:
1772 # Single file import
1773 return self.unit_import(self.filepath, import_settings)
1775 def unit_import(self, filename, import_settings):
1776 import time
1777 from .io.imp.gltf2_io_gltf import glTFImporter, ImportError
1778 from .blender.imp.gltf2_blender_gltf import BlenderGlTF
1780 try:
1781 gltf_importer = glTFImporter(filename, import_settings)
1782 gltf_importer.read()
1783 gltf_importer.checks()
1785 gltf_importer.log.info("Data are loaded, start creating Blender stuff")
1787 start_time = time.time()
1788 BlenderGlTF.create(gltf_importer)
1789 elapsed_s = "{:.2f}s".format(time.time() - start_time)
1790 gltf_importer.log.info("glTF import finished in " + elapsed_s)
1792 # Display popup log, if any
1793 for message_type, message in gltf_importer.log.messages():
1794 self.report({message_type}, message)
1796 gltf_importer.log.flush()
1798 return {'FINISHED'}
1800 except ImportError as e:
1801 self.report({'ERROR'}, e.args[0])
1802 return {'CANCELLED'}
1804 def set_debug_log(self):
1805 import logging
1806 if bpy.app.debug_value == 0: # Default values => Display all messages except debug ones
1807 self.loglevel = logging.INFO
1808 elif bpy.app.debug_value == 1:
1809 self.loglevel = logging.WARNING
1810 elif bpy.app.debug_value == 2:
1811 self.loglevel = logging.ERROR
1812 elif bpy.app.debug_value == 3:
1813 self.loglevel = logging.CRITICAL
1814 elif bpy.app.debug_value == 4:
1815 self.loglevel = logging.DEBUG
1818 class GLTF2_filter_action(bpy.types.PropertyGroup):
1819 keep : bpy.props.BoolProperty(name="Keep Animation")
1820 action: bpy.props.PointerProperty(type=bpy.types.Action)
1822 def gltf_variant_ui_update(self, context):
1823 from .blender.com.gltf2_blender_ui import variant_register, variant_unregister
1824 if self.KHR_materials_variants_ui is True:
1825 # register all needed types
1826 variant_register()
1827 else:
1828 variant_unregister()
1830 def gltf_animation_ui_update(self, context):
1831 from .blender.com.gltf2_blender_ui import anim_ui_register, anim_ui_unregister
1832 if self.animation_ui is True:
1833 # register all needed types
1834 anim_ui_register()
1835 else:
1836 anim_ui_unregister()
1838 class GLTF_AddonPreferences(bpy.types.AddonPreferences):
1839 bl_idname = __package__
1841 settings_node_ui : bpy.props.BoolProperty(
1842 default= False,
1843 description="Displays glTF Material Output node in Shader Editor (Menu Add > Output)"
1846 KHR_materials_variants_ui : bpy.props.BoolProperty(
1847 default= False,
1848 description="Displays glTF UI to manage material variants",
1849 update=gltf_variant_ui_update
1852 animation_ui: bpy.props.BoolProperty(
1853 default=False,
1854 description="Display glTF UI to manage animations",
1855 update=gltf_animation_ui_update
1858 gltfpack_path_ui: bpy.props.StringProperty(
1859 default="",
1860 name="glTFpack file path",
1861 description="Path to gltfpack binary",
1862 subtype='FILE_PATH'
1865 allow_embedded_format: bpy.props.BoolProperty(
1866 default = False,
1867 name='Allow glTF Embedded format',
1868 description="Allow glTF Embedded format"
1871 def draw(self, context):
1872 layout = self.layout
1873 row = layout.row()
1874 row.prop(self, "settings_node_ui", text="Shader Editor Add-ons")
1875 row.prop(self, "KHR_materials_variants_ui", text="Material Variants")
1876 row.prop(self, "animation_ui", text="Animation UI")
1877 row = layout.row()
1878 row.prop(self, "gltfpack_path_ui", text="Path to gltfpack")
1879 row = layout.row()
1880 row.prop(self, "allow_embedded_format", text="Allow glTF Embedded format")
1881 if self.allow_embedded_format:
1882 layout.label(text="This is the least efficient of the available forms, and should only be used when required.", icon='ERROR')
1885 class IO_FH_gltf2(bpy.types.FileHandler):
1886 bl_idname = "IO_FH_gltf2"
1887 bl_label = "glTF 2.0"
1888 bl_import_operator = "import_scene.gltf"
1889 bl_export_operator = "export_scene.gltf"
1890 bl_file_extensions = ".glb;.gltf"
1892 @classmethod
1893 def poll_drop(cls, context):
1894 return poll_file_object_drop(context)
1897 def menu_func_import(self, context):
1898 self.layout.operator(ImportGLTF2.bl_idname, text='glTF 2.0 (.glb/.gltf)')
1901 classes = (
1902 ExportGLTF2,
1903 ImportGLTF2,
1904 IO_FH_gltf2,
1905 GLTF2_filter_action,
1906 GLTF_AddonPreferences
1910 def register():
1911 from .blender.com import gltf2_blender_ui as blender_ui
1913 for c in classes:
1914 bpy.utils.register_class(c)
1915 # bpy.utils.register_module(__name__)
1917 blender_ui.register()
1918 if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True:
1919 blender_ui.variant_register()
1920 if bpy.context.preferences.addons['io_scene_gltf2'].preferences.animation_ui is True:
1921 blender_ui.anim_ui_register()
1923 # add to the export / import menu
1924 bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
1925 bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
1928 def unregister():
1929 from .blender.com import gltf2_blender_ui as blender_ui
1930 blender_ui.unregister()
1931 if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True:
1932 blender_ui.variant_unregister()
1934 for c in classes:
1935 bpy.utils.unregister_class(c)
1936 for f in exporter_extension_panel_unregister_functors:
1938 exporter_extension_panel_unregister_functors.clear()
1940 for f in importer_extension_panel_unregister_functors:
1942 importer_extension_panel_unregister_functors.clear()
1944 # bpy.utils.unregister_module(__name__)
1946 # remove from the export / import menu
1947 bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
1948 bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)