FBX IO: Vertex position access with attributes
[blender-addons.git] / io_scene_gltf2 / __init__.py
blob34907645ec0b604f8df9b515c12ac0f8034ed033
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, 0, 4),
9 'blender': (3, 5, 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 and JPEGs as JPEGs. '
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 ('NONE', 'None',
183 'Don\'t export images'),
185 description=(
186 'Output format for images. PNG is lossless and generally preferred, but JPEG might be preferable for web '
187 'applications due to the smaller file size. Alternatively they can be omitted if they are not needed'
189 default='AUTO'
192 export_texture_dir: StringProperty(
193 name='Textures',
194 description='Folder to place texture files in. Relative to the .gltf file',
195 default='',
198 export_jpeg_quality: IntProperty(
199 name='JPEG quality',
200 description='Quality of JPEG export',
201 default=75,
202 min=0,
203 max=100
206 export_keep_originals: BoolProperty(
207 name='Keep original',
208 description=('Keep original textures files if possible. '
209 'WARNING: if you use more than one texture, '
210 'where pbr standard requires only one, only one texture will be used. '
211 'This can lead to unexpected results'
213 default=False,
216 export_texcoords: BoolProperty(
217 name='UVs',
218 description='Export UVs (texture coordinates) with meshes',
219 default=True
222 export_normals: BoolProperty(
223 name='Normals',
224 description='Export vertex normals with meshes',
225 default=True
228 export_draco_mesh_compression_enable: BoolProperty(
229 name='Draco mesh compression',
230 description='Compress mesh using Draco',
231 default=False
234 export_draco_mesh_compression_level: IntProperty(
235 name='Compression level',
236 description='Compression level (0 = most speed, 6 = most compression, higher values currently not supported)',
237 default=6,
238 min=0,
239 max=10
242 export_draco_position_quantization: IntProperty(
243 name='Position quantization bits',
244 description='Quantization bits for position values (0 = no quantization)',
245 default=14,
246 min=0,
247 max=30
250 export_draco_normal_quantization: IntProperty(
251 name='Normal quantization bits',
252 description='Quantization bits for normal values (0 = no quantization)',
253 default=10,
254 min=0,
255 max=30
258 export_draco_texcoord_quantization: IntProperty(
259 name='Texcoord quantization bits',
260 description='Quantization bits for texture coordinate values (0 = no quantization)',
261 default=12,
262 min=0,
263 max=30
266 export_draco_color_quantization: IntProperty(
267 name='Color quantization bits',
268 description='Quantization bits for color values (0 = no quantization)',
269 default=10,
270 min=0,
271 max=30
274 export_draco_generic_quantization: IntProperty(
275 name='Generic quantization bits',
276 description='Quantization bits for generic values like weights or joints (0 = no quantization)',
277 default=12,
278 min=0,
279 max=30
282 export_tangents: BoolProperty(
283 name='Tangents',
284 description='Export vertex tangents with meshes',
285 default=False
288 export_materials: EnumProperty(
289 name='Materials',
290 items=(('EXPORT', 'Export',
291 'Export all materials used by included objects'),
292 ('PLACEHOLDER', 'Placeholder',
293 'Do not export materials, but write multiple primitive groups per mesh, keeping material slot information'),
294 ('NONE', 'No export',
295 'Do not export materials, and combine mesh primitive groups, losing material slot information')),
296 description='Export materials',
297 default='EXPORT'
300 export_original_specular: BoolProperty(
301 name='Export original PBR Specular',
302 description=(
303 'Export original glTF PBR Specular, instead of Blender Principled Shader Specular'
305 default=False,
308 export_colors: BoolProperty(
309 name='Vertex Colors',
310 description='Export vertex colors with meshes',
311 default=True
314 export_attributes: BoolProperty(
315 name='Attributes',
316 description='Export Attributes (when starting with underscore)',
317 default=False
320 use_mesh_edges: BoolProperty(
321 name='Loose Edges',
322 description=(
323 'Export loose edges as lines, using the material from the first material slot'
325 default=False,
328 use_mesh_vertices: BoolProperty(
329 name='Loose Points',
330 description=(
331 'Export loose points as glTF points, using the material from the first material slot'
333 default=False,
336 export_cameras: BoolProperty(
337 name='Cameras',
338 description='Export cameras',
339 default=False
342 use_selection: BoolProperty(
343 name='Selected Objects',
344 description='Export selected objects only',
345 default=False
348 use_visible: BoolProperty(
349 name='Visible Objects',
350 description='Export visible objects only',
351 default=False
354 use_renderable: BoolProperty(
355 name='Renderable Objects',
356 description='Export renderable objects only',
357 default=False
360 use_active_collection_with_nested: BoolProperty(
361 name='Include Nested Collections',
362 description='Include active collection and nested collections',
363 default=True
366 use_active_collection: BoolProperty(
367 name='Active Collection',
368 description='Export objects in the active collection only',
369 default=False
372 use_active_scene: BoolProperty(
373 name='Active Scene',
374 description='Export active scene only',
375 default=False
378 export_extras: BoolProperty(
379 name='Custom Properties',
380 description='Export custom properties as glTF extras',
381 default=False
384 export_yup: BoolProperty(
385 name='+Y Up',
386 description='Export using glTF convention, +Y up',
387 default=True
390 export_apply: BoolProperty(
391 name='Apply Modifiers',
392 description='Apply modifiers (excluding Armatures) to mesh objects -'
393 'WARNING: prevents exporting shape keys',
394 default=False
397 export_animations: BoolProperty(
398 name='Animations',
399 description='Exports active actions and NLA tracks as glTF animations',
400 default=True
403 export_frame_range: BoolProperty(
404 name='Limit to Playback Range',
405 description='Clips animations to selected playback range',
406 default=False
409 export_frame_step: IntProperty(
410 name='Sampling Rate',
411 description='How often to evaluate animated values (in frames)',
412 default=1,
413 min=1,
414 max=120
417 export_force_sampling: BoolProperty(
418 name='Always Sample Animations',
419 description='Apply sampling to all animations',
420 default=True
423 export_animation_mode: EnumProperty(
424 name='Animation mode',
425 items=(('ACTIONS', 'Actions',
426 'Export actions (actives and on NLA tracks) as separate animations'),
427 ('ACTIVE_ACTIONS', 'Active actions merged',
428 'All the currently assigned actions become one glTF animation'),
429 ('NLA_TRACKS', 'NLA Tracks',
430 'Export individual NLA Tracks as separate animation'),
431 ('SCENE', 'Scene',
432 'Export baked scene as a single animation')
434 description='Export Animation mode',
435 default='ACTIONS'
438 export_nla_strips_merged_animation_name: StringProperty(
439 name='Merged Animation Name',
440 description=(
441 "Name of single glTF animation to be exported"
443 default='Animation'
446 export_def_bones: BoolProperty(
447 name='Export Deformation Bones Only',
448 description='Export Deformation bones only',
449 default=False
452 export_hierarchy_flatten_bones: BoolProperty(
453 name='Flatten Bone Hierarchy',
454 description='Flatten Bone Hierarchy. Useful in case of non decomposable transformation matrix',
455 default=False
458 export_optimize_animation_size: BoolProperty(
459 name='Optimize Animation Size',
460 description=(
461 "Reduce exported file size by removing duplicate keyframes"
463 default=True
466 export_optimize_animation_keep_anim_armature: BoolProperty(
467 name='Force keeping channels for bones',
468 description=(
469 "if all keyframes are identical in a rig, "
470 "force keeping the minimal animation. "
471 "When off, all possible channels for "
472 "the bones will be exported, even if empty "
473 "(minimal animation, 2 keyframes)"
475 default=True
478 export_optimize_animation_keep_anim_object: BoolProperty(
479 name='Force keeping channel for objects',
480 description=(
481 "If all keyframes are identical for object transformations, "
482 "force keeping the minimal animation"
484 default=False
487 export_negative_frame: EnumProperty(
488 name='Negative Frames',
489 items=(('SLIDE', 'Slide',
490 'Slide animation to start at frame 0'),
491 ('CROP', 'Crop',
492 'Keep only frames above frame 0'),
494 description='Negative Frames are slid or cropped',
495 default='SLIDE'
498 export_anim_slide_to_zero: BoolProperty(
499 name='Set all glTF Animation starting at 0',
500 description=(
501 "Set all glTF animation starting at 0.0s. "
502 "Can be useful for looping animations"
504 default=False
507 export_bake_animation: BoolProperty(
508 name='Bake All Objects Animations',
509 description=(
510 "Force exporting animation on every object. "
511 "Can be useful when using constraints or driver. "
512 "Also useful when exporting only selection"
514 default=False
517 export_anim_single_armature: BoolProperty(
518 name='Export all Armature Actions',
519 description=(
520 "Export all actions, bound to a single armature. "
521 "WARNING: Option does not support exports including multiple armatures"
523 default=True
526 export_reset_pose_bones: BoolProperty(
527 name='Reset pose bones between actions',
528 description=(
529 "Reset pose bones between each action exported. "
530 "This is needed when some bones are not keyed on some animations"
532 default=True
535 export_current_frame: BoolProperty(
536 name='Use Current Frame as Object Rest Transformations',
537 description=(
538 'Export the scene in the current animation frame. '
539 'When off, frame 0 is used as rest transformations for objects'
541 default=False
544 export_rest_position_armature: BoolProperty(
545 name='Use Rest Position Armature',
546 description=(
547 "Export armatures using rest position as joints' rest pose. "
548 "When off, current frame pose is used as rest pose"
550 default=True
553 export_anim_scene_split_object: BoolProperty(
554 name='Split Animation by Object',
555 description=(
556 "Export Scene as seen in Viewport, "
557 "But split animation by Object"
559 default=True
562 export_skins: BoolProperty(
563 name='Skinning',
564 description='Export skinning (armature) data',
565 default=True
568 export_all_influences: BoolProperty(
569 name='Include All Bone Influences',
570 description='Allow >4 joint vertex influences. Models may appear incorrectly in many viewers',
571 default=False
574 export_morph: BoolProperty(
575 name='Shape Keys',
576 description='Export shape keys (morph targets)',
577 default=True
580 export_morph_normal: BoolProperty(
581 name='Shape Key Normals',
582 description='Export vertex normals with shape keys (morph targets)',
583 default=True
586 export_morph_tangent: BoolProperty(
587 name='Shape Key Tangents',
588 description='Export vertex tangents with shape keys (morph targets)',
589 default=False
592 export_morph_animation: BoolProperty(
593 name='Shape Key Animations',
594 description='Export shape keys animations (morph targets)',
595 default=True
598 export_morph_reset_sk_data: BoolProperty(
599 name='Reset shape keys between actions',
600 description=(
601 "Reset shape keys between each action exported. "
602 "This is needed when some SK channels are not keyed on some animations"
604 default=True
607 export_lights: BoolProperty(
608 name='Punctual Lights',
609 description='Export directional, point, and spot lights. '
610 'Uses "KHR_lights_punctual" glTF extension',
611 default=False
614 # This parameter is only here for backward compatibility, as this option is removed in 3.6
615 # This option does nothing, and is not displayed in UI
616 # What you are looking for is probably "export_animation_mode"
617 export_nla_strips: BoolProperty(
618 name='Group by NLA Track',
619 description=(
620 "When on, multiple actions become part of the same glTF animation if "
621 "they're pushed onto NLA tracks with the same name. "
622 "When off, all the currently assigned actions become one glTF animation"
624 default=True
627 will_save_settings: BoolProperty(
628 name='Remember Export Settings',
629 description='Store glTF export settings in the Blender project',
630 default=False)
632 # Custom scene property for saving settings
633 scene_key = "glTF2ExportSettings"
637 def check(self, _context):
638 # Ensure file extension matches format
639 old_filepath = self.filepath
640 self.filepath = ensure_filepath_matches_export_format(
641 self.filepath,
642 self.export_format,
644 return self.filepath != old_filepath
646 def invoke(self, context, event):
647 settings = context.scene.get(self.scene_key)
648 self.will_save_settings = False
649 if settings:
650 try:
651 for (k, v) in settings.items():
652 setattr(self, k, v)
653 self.will_save_settings = True
655 # Update filter if user saved settings
656 if hasattr(self, 'export_format'):
657 self.filter_glob = '*.glb' if self.export_format == 'GLB' else '*.gltf'
659 except (AttributeError, TypeError):
660 self.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings")
661 del context.scene[self.scene_key]
663 import sys
664 preferences = bpy.context.preferences
665 for addon_name in preferences.addons.keys():
666 try:
667 if hasattr(sys.modules[addon_name], 'glTF2ExportUserExtension') or hasattr(sys.modules[addon_name], 'glTF2ExportUserExtensions'):
668 exporter_extension_panel_unregister_functors.append(sys.modules[addon_name].register_panel())
669 except Exception:
670 pass
672 self.has_active_exporter_extensions = len(exporter_extension_panel_unregister_functors) > 0
673 return ExportHelper.invoke(self, context, event)
675 def save_settings(self, context):
676 # find all props to save
677 exceptional = [
678 # options that don't start with 'export_'
679 'use_selection',
680 'use_visible',
681 'use_renderable',
682 'use_active_collection_with_nested',
683 'use_active_collection',
684 'use_mesh_edges',
685 'use_mesh_vertices',
686 'use_active_scene',
688 all_props = self.properties
689 export_props = {
690 x: getattr(self, x) for x in dir(all_props)
691 if (x.startswith("export_") or x in exceptional) and all_props.get(x) is not None
693 context.scene[self.scene_key] = export_props
695 def execute(self, context):
696 import os
697 import datetime
698 from .blender.exp import gltf2_blender_export
699 from .io.com.gltf2_io_path import path_to_uri
701 if self.will_save_settings:
702 self.save_settings(context)
704 self.check(context) # ensure filepath has the right extension
706 # All custom export settings are stored in this container.
707 export_settings = {}
709 export_settings['timestamp'] = datetime.datetime.now()
710 export_settings['gltf_export_id'] = self.gltf_export_id
711 export_settings['gltf_filepath'] = self.filepath
712 export_settings['gltf_filedirectory'] = os.path.dirname(export_settings['gltf_filepath']) + '/'
713 export_settings['gltf_texturedirectory'] = os.path.join(
714 export_settings['gltf_filedirectory'],
715 self.export_texture_dir,
717 export_settings['gltf_keep_original_textures'] = self.export_keep_originals
719 export_settings['gltf_format'] = self.export_format
720 export_settings['gltf_image_format'] = self.export_image_format
721 export_settings['gltf_jpeg_quality'] = self.export_jpeg_quality
722 export_settings['gltf_copyright'] = self.export_copyright
723 export_settings['gltf_texcoords'] = self.export_texcoords
724 export_settings['gltf_normals'] = self.export_normals
725 export_settings['gltf_tangents'] = self.export_tangents and self.export_normals
726 export_settings['gltf_loose_edges'] = self.use_mesh_edges
727 export_settings['gltf_loose_points'] = self.use_mesh_vertices
729 if self.is_draco_available:
730 export_settings['gltf_draco_mesh_compression'] = self.export_draco_mesh_compression_enable
731 export_settings['gltf_draco_mesh_compression_level'] = self.export_draco_mesh_compression_level
732 export_settings['gltf_draco_position_quantization'] = self.export_draco_position_quantization
733 export_settings['gltf_draco_normal_quantization'] = self.export_draco_normal_quantization
734 export_settings['gltf_draco_texcoord_quantization'] = self.export_draco_texcoord_quantization
735 export_settings['gltf_draco_color_quantization'] = self.export_draco_color_quantization
736 export_settings['gltf_draco_generic_quantization'] = self.export_draco_generic_quantization
737 else:
738 export_settings['gltf_draco_mesh_compression'] = False
740 export_settings['gltf_materials'] = self.export_materials
741 export_settings['gltf_colors'] = self.export_colors
742 export_settings['gltf_attributes'] = self.export_attributes
743 export_settings['gltf_cameras'] = self.export_cameras
745 export_settings['gltf_original_specular'] = self.export_original_specular
747 export_settings['gltf_visible'] = self.use_visible
748 export_settings['gltf_renderable'] = self.use_renderable
750 export_settings['gltf_active_collection'] = self.use_active_collection
751 if self.use_active_collection:
752 export_settings['gltf_active_collection_with_nested'] = self.use_active_collection_with_nested
753 else:
754 export_settings['gltf_active_collection_with_nested'] = False
755 export_settings['gltf_active_scene'] = self.use_active_scene
757 export_settings['gltf_selected'] = self.use_selection
758 export_settings['gltf_layers'] = True # self.export_layers
759 export_settings['gltf_extras'] = self.export_extras
760 export_settings['gltf_yup'] = self.export_yup
761 export_settings['gltf_apply'] = self.export_apply
762 export_settings['gltf_current_frame'] = self.export_current_frame
763 export_settings['gltf_animations'] = self.export_animations
764 export_settings['gltf_def_bones'] = self.export_def_bones
765 export_settings['gltf_flatten_bones_hierarchy'] = self.export_hierarchy_flatten_bones
766 if self.export_animations:
767 export_settings['gltf_frame_range'] = self.export_frame_range
768 export_settings['gltf_force_sampling'] = self.export_force_sampling
769 if not self.export_force_sampling:
770 export_settings['gltf_def_bones'] = False
771 export_settings['gltf_bake_animation'] = False
772 export_settings['gltf_animation_mode'] = self.export_animation_mode
773 if export_settings['gltf_animation_mode'] == "NLA_TRACKS":
774 export_settings['gltf_force_sampling'] = True
775 if export_settings['gltf_animation_mode'] == "SCENE":
776 export_settings['gltf_anim_scene_split_object'] = self.export_anim_scene_split_object
777 else:
778 export_settings['gltf_anim_scene_split_object'] = False
780 export_settings['gltf_nla_strips_merged_animation_name'] = self.export_nla_strips_merged_animation_name
781 export_settings['gltf_optimize_animation'] = self.export_optimize_animation_size
782 export_settings['gltf_optimize_animation_keep_armature'] = self.export_optimize_animation_keep_anim_armature
783 export_settings['gltf_optimize_animation_keep_object'] = self.export_optimize_animation_keep_anim_object
784 export_settings['gltf_export_anim_single_armature'] = self.export_anim_single_armature
785 export_settings['gltf_export_reset_pose_bones'] = self.export_reset_pose_bones
786 export_settings['gltf_export_reset_sk_data'] = self.export_morph_reset_sk_data
787 export_settings['gltf_bake_animation'] = self.export_bake_animation
788 export_settings['gltf_negative_frames'] = self.export_negative_frame
789 export_settings['gltf_anim_slide_to_zero'] = self.export_anim_slide_to_zero
790 else:
791 export_settings['gltf_frame_range'] = False
792 export_settings['gltf_force_sampling'] = False
793 export_settings['gltf_bake_animation'] = False
794 export_settings['gltf_optimize_animation'] = False
795 export_settings['gltf_optimize_animation_keep_armature'] = False
796 export_settings['gltf_optimize_animation_keep_object'] = False
797 export_settings['gltf_export_anim_single_armature'] = False
798 export_settings['gltf_export_reset_pose_bones'] = False
799 export_settings['gltf_export_reset_sk_data'] = False
800 export_settings['gltf_skins'] = self.export_skins
801 if self.export_skins:
802 export_settings['gltf_all_vertex_influences'] = self.export_all_influences
803 else:
804 export_settings['gltf_all_vertex_influences'] = False
805 export_settings['gltf_def_bones'] = False
806 export_settings['gltf_rest_position_armature'] = self.export_rest_position_armature
807 export_settings['gltf_frame_step'] = self.export_frame_step
809 export_settings['gltf_morph'] = self.export_morph
810 if self.export_morph:
811 export_settings['gltf_morph_normal'] = self.export_morph_normal
812 export_settings['gltf_morph_tangent'] = self.export_morph_tangent
813 export_settings['gltf_morph_anim'] = self.export_morph_animation
814 else:
815 export_settings['gltf_morph_normal'] = False
816 export_settings['gltf_morph_tangent'] = False
817 export_settings['gltf_morph_anim'] = False
819 export_settings['gltf_lights'] = self.export_lights
820 export_settings['gltf_lighting_mode'] = self.export_import_convert_lighting_mode
822 export_settings['gltf_binary'] = bytearray()
823 export_settings['gltf_binaryfilename'] = (
824 path_to_uri(os.path.splitext(os.path.basename(self.filepath))[0] + '.bin')
827 user_extensions = []
828 pre_export_callbacks = []
829 post_export_callbacks = []
831 import sys
832 preferences = bpy.context.preferences
833 for addon_name in preferences.addons.keys():
834 try:
835 module = sys.modules[addon_name]
836 except Exception:
837 continue
838 if hasattr(module, 'glTF2ExportUserExtension'):
839 extension_ctor = module.glTF2ExportUserExtension
840 user_extensions.append(extension_ctor())
841 if hasattr(module, 'glTF2ExportUserExtensions'):
842 extension_ctors = module.glTF2ExportUserExtensions
843 for extension_ctor in extension_ctors:
844 user_extensions.append(extension_ctor())
845 if hasattr(module, 'glTF2_pre_export_callback'):
846 pre_export_callbacks.append(module.glTF2_pre_export_callback)
847 if hasattr(module, 'glTF2_post_export_callback'):
848 post_export_callbacks.append(module.glTF2_post_export_callback)
849 export_settings['gltf_user_extensions'] = user_extensions
850 export_settings['pre_export_callbacks'] = pre_export_callbacks
851 export_settings['post_export_callbacks'] = post_export_callbacks
853 return gltf2_blender_export.save(context, export_settings)
855 def draw(self, context):
856 pass # Is needed to get panels available
859 class GLTF_PT_export_main(bpy.types.Panel):
860 bl_space_type = 'FILE_BROWSER'
861 bl_region_type = 'TOOL_PROPS'
862 bl_label = ""
863 bl_parent_id = "FILE_PT_operator"
864 bl_options = {'HIDE_HEADER'}
866 @classmethod
867 def poll(cls, context):
868 sfile = context.space_data
869 operator = sfile.active_operator
871 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
873 def draw(self, context):
874 layout = self.layout
875 layout.use_property_split = True
876 layout.use_property_decorate = False # No animation.
878 sfile = context.space_data
879 operator = sfile.active_operator
881 layout.prop(operator, 'export_format')
882 if operator.export_format == 'GLTF_SEPARATE':
883 layout.prop(operator, 'export_keep_originals')
884 if operator.export_keep_originals is False:
885 layout.prop(operator, 'export_texture_dir', icon='FILE_FOLDER')
887 layout.prop(operator, 'export_copyright')
888 layout.prop(operator, 'will_save_settings')
891 class GLTF_PT_export_include(bpy.types.Panel):
892 bl_space_type = 'FILE_BROWSER'
893 bl_region_type = 'TOOL_PROPS'
894 bl_label = "Include"
895 bl_parent_id = "FILE_PT_operator"
896 bl_options = {'DEFAULT_CLOSED'}
898 @classmethod
899 def poll(cls, context):
900 sfile = context.space_data
901 operator = sfile.active_operator
903 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
905 def draw(self, context):
906 layout = self.layout
907 layout.use_property_split = True
908 layout.use_property_decorate = False # No animation.
910 sfile = context.space_data
911 operator = sfile.active_operator
913 col = layout.column(heading = "Limit to", align = True)
914 col.prop(operator, 'use_selection')
915 col.prop(operator, 'use_visible')
916 col.prop(operator, 'use_renderable')
917 col.prop(operator, 'use_active_collection')
918 if operator.use_active_collection:
919 col.prop(operator, 'use_active_collection_with_nested')
920 col.prop(operator, 'use_active_scene')
922 col = layout.column(heading = "Data", align = True)
923 col.prop(operator, 'export_extras')
924 col.prop(operator, 'export_cameras')
925 col.prop(operator, 'export_lights')
928 class GLTF_PT_export_transform(bpy.types.Panel):
929 bl_space_type = 'FILE_BROWSER'
930 bl_region_type = 'TOOL_PROPS'
931 bl_label = "Transform"
932 bl_parent_id = "FILE_PT_operator"
933 bl_options = {'DEFAULT_CLOSED'}
935 @classmethod
936 def poll(cls, context):
937 sfile = context.space_data
938 operator = sfile.active_operator
940 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
942 def draw(self, context):
943 layout = self.layout
944 layout.use_property_split = True
945 layout.use_property_decorate = False # No animation.
947 sfile = context.space_data
948 operator = sfile.active_operator
950 layout.prop(operator, 'export_yup')
953 class GLTF_PT_export_data(bpy.types.Panel):
954 bl_space_type = 'FILE_BROWSER'
955 bl_region_type = 'TOOL_PROPS'
956 bl_label = "Data"
957 bl_parent_id = "FILE_PT_operator"
958 bl_options = {'DEFAULT_CLOSED'}
960 @classmethod
961 def poll(cls, context):
962 sfile = context.space_data
963 operator = sfile.active_operator
965 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
967 def draw(self, context):
968 pass
970 class GLTF_PT_export_data_mesh(bpy.types.Panel):
971 bl_space_type = 'FILE_BROWSER'
972 bl_region_type = 'TOOL_PROPS'
973 bl_label = "Mesh"
974 bl_parent_id = "GLTF_PT_export_data"
975 bl_options = {'DEFAULT_CLOSED'}
977 @classmethod
978 def poll(cls, context):
979 sfile = context.space_data
980 operator = sfile.active_operator
981 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
983 def draw(self, context):
984 layout = self.layout
985 layout.use_property_split = True
986 layout.use_property_decorate = False # No animation.
988 sfile = context.space_data
989 operator = sfile.active_operator
991 layout.prop(operator, 'export_apply')
992 layout.prop(operator, 'export_texcoords')
993 layout.prop(operator, 'export_normals')
994 col = layout.column()
995 col.active = operator.export_normals
996 col.prop(operator, 'export_tangents')
997 layout.prop(operator, 'export_colors')
998 layout.prop(operator, 'export_attributes')
1000 col = layout.column()
1001 col.prop(operator, 'use_mesh_edges')
1002 col.prop(operator, 'use_mesh_vertices')
1005 class GLTF_PT_export_data_material(bpy.types.Panel):
1006 bl_space_type = 'FILE_BROWSER'
1007 bl_region_type = 'TOOL_PROPS'
1008 bl_label = "Material"
1009 bl_parent_id = "GLTF_PT_export_data"
1010 bl_options = {'DEFAULT_CLOSED'}
1012 @classmethod
1013 def poll(cls, context):
1014 sfile = context.space_data
1015 operator = sfile.active_operator
1016 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1018 def draw(self, context):
1019 layout = self.layout
1020 layout.use_property_split = True
1021 layout.use_property_decorate = False # No animation.
1023 sfile = context.space_data
1024 operator = sfile.active_operator
1026 layout.prop(operator, 'export_materials')
1027 col = layout.column()
1028 col.active = operator.export_materials == "EXPORT"
1029 col.prop(operator, 'export_image_format')
1030 if operator.export_image_format in ["AUTO", "JPEG"]:
1031 col.prop(operator, 'export_jpeg_quality')
1033 class GLTF_PT_export_data_original_pbr(bpy.types.Panel):
1034 bl_space_type = 'FILE_BROWSER'
1035 bl_region_type = 'TOOL_PROPS'
1036 bl_label = "PBR Extensions"
1037 bl_parent_id = "GLTF_PT_export_data_material"
1038 bl_options = {'DEFAULT_CLOSED'}
1040 @classmethod
1041 def poll(cls, context):
1042 sfile = context.space_data
1043 operator = sfile.active_operator
1044 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1046 def draw(self, context):
1047 layout = self.layout
1048 layout.use_property_split = True
1049 layout.use_property_decorate = False # No animation.
1051 sfile = context.space_data
1052 operator = sfile.active_operator
1054 layout.prop(operator, 'export_original_specular')
1056 class GLTF_PT_export_data_lighting(bpy.types.Panel):
1057 bl_space_type = 'FILE_BROWSER'
1058 bl_region_type = 'TOOL_PROPS'
1059 bl_label = "Lighting"
1060 bl_parent_id = "GLTF_PT_export_data"
1061 bl_options = {'DEFAULT_CLOSED'}
1063 @classmethod
1064 def poll(cls, context):
1065 sfile = context.space_data
1066 operator = sfile.active_operator
1067 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1069 def draw(self, context):
1070 layout = self.layout
1071 layout.use_property_split = True
1072 layout.use_property_decorate = False # No animation.
1074 sfile = context.space_data
1075 operator = sfile.active_operator
1077 layout.prop(operator, 'export_import_convert_lighting_mode')
1079 class GLTF_PT_export_data_shapekeys(bpy.types.Panel):
1080 bl_space_type = 'FILE_BROWSER'
1081 bl_region_type = 'TOOL_PROPS'
1082 bl_label = "Shape Keys"
1083 bl_parent_id = "GLTF_PT_export_data"
1084 bl_options = {'DEFAULT_CLOSED'}
1086 @classmethod
1087 def poll(cls, context):
1088 sfile = context.space_data
1089 operator = sfile.active_operator
1091 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1093 def draw_header(self, context):
1094 sfile = context.space_data
1095 operator = sfile.active_operator
1096 self.layout.prop(operator, "export_morph", text="")
1098 def draw(self, context):
1099 layout = self.layout
1100 layout.use_property_split = True
1101 layout.use_property_decorate = False # No animation.
1103 sfile = context.space_data
1104 operator = sfile.active_operator
1106 layout.active = operator.export_morph
1108 layout.prop(operator, 'export_morph_normal')
1109 col = layout.column()
1110 col.active = operator.export_morph_normal
1111 col.prop(operator, 'export_morph_tangent')
1114 class GLTF_PT_export_data_skinning(bpy.types.Panel):
1115 bl_space_type = 'FILE_BROWSER'
1116 bl_region_type = 'TOOL_PROPS'
1117 bl_label = "Skinning"
1118 bl_parent_id = "GLTF_PT_export_data"
1119 bl_options = {'DEFAULT_CLOSED'}
1121 @classmethod
1122 def poll(cls, context):
1123 sfile = context.space_data
1124 operator = sfile.active_operator
1126 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1128 def draw_header(self, context):
1129 sfile = context.space_data
1130 operator = sfile.active_operator
1131 self.layout.prop(operator, "export_skins", text="")
1133 def draw(self, context):
1134 layout = self.layout
1135 layout.use_property_split = True
1136 layout.use_property_decorate = False # No animation.
1138 sfile = context.space_data
1139 operator = sfile.active_operator
1141 layout.active = operator.export_skins
1143 layout.prop(operator, 'export_all_influences')
1146 class GLTF_PT_export_data_armature(bpy.types.Panel):
1147 bl_space_type = 'FILE_BROWSER'
1148 bl_region_type = 'TOOL_PROPS'
1149 bl_label = "Armature"
1150 bl_parent_id = "GLTF_PT_export_data"
1151 bl_options = {'DEFAULT_CLOSED'}
1153 @classmethod
1154 def poll(cls, context):
1155 sfile = context.space_data
1156 operator = sfile.active_operator
1158 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1160 def draw(self, context):
1161 layout = self.layout
1162 layout.use_property_split = True
1163 layout.use_property_decorate = False # No animation.
1165 sfile = context.space_data
1166 operator = sfile.active_operator
1168 layout.active = operator.export_skins
1170 layout.prop(operator, 'export_rest_position_armature')
1172 row = layout.row()
1173 row.active = operator.export_force_sampling
1174 row.prop(operator, 'export_def_bones')
1175 if operator.export_force_sampling is False and operator.export_def_bones is True:
1176 layout.label(text="Export only deformation bones is not possible when not sampling animation")
1177 row = layout.row()
1178 row.prop(operator, 'export_hierarchy_flatten_bones')
1180 class GLTF_PT_export_data_compression(bpy.types.Panel):
1181 bl_space_type = 'FILE_BROWSER'
1182 bl_region_type = 'TOOL_PROPS'
1183 bl_label = "Compression"
1184 bl_parent_id = "GLTF_PT_export_data"
1185 bl_options = {'DEFAULT_CLOSED'}
1187 def __init__(self):
1188 from .io.com import gltf2_io_draco_compression_extension
1189 self.is_draco_available = gltf2_io_draco_compression_extension.dll_exists(quiet=True)
1191 @classmethod
1192 def poll(cls, context):
1193 sfile = context.space_data
1194 operator = sfile.active_operator
1195 if operator.is_draco_available:
1196 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1198 def draw_header(self, context):
1199 sfile = context.space_data
1200 operator = sfile.active_operator
1201 self.layout.prop(operator, "export_draco_mesh_compression_enable", text="")
1203 def draw(self, context):
1204 layout = self.layout
1205 layout.use_property_split = True
1206 layout.use_property_decorate = False # No animation.
1208 sfile = context.space_data
1209 operator = sfile.active_operator
1211 layout.active = operator.export_draco_mesh_compression_enable
1212 layout.prop(operator, 'export_draco_mesh_compression_level')
1214 col = layout.column(align=True)
1215 col.prop(operator, 'export_draco_position_quantization', text="Quantize Position")
1216 col.prop(operator, 'export_draco_normal_quantization', text="Normal")
1217 col.prop(operator, 'export_draco_texcoord_quantization', text="Tex Coord")
1218 col.prop(operator, 'export_draco_color_quantization', text="Color")
1219 col.prop(operator, 'export_draco_generic_quantization', text="Generic")
1222 class GLTF_PT_export_animation(bpy.types.Panel):
1223 bl_space_type = 'FILE_BROWSER'
1224 bl_region_type = 'TOOL_PROPS'
1225 bl_label = "Animation"
1226 bl_parent_id = "FILE_PT_operator"
1227 bl_options = {'DEFAULT_CLOSED'}
1229 @classmethod
1230 def poll(cls, context):
1231 sfile = context.space_data
1232 operator = sfile.active_operator
1234 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1236 def draw_header(self, context):
1237 sfile = context.space_data
1238 operator = sfile.active_operator
1239 self.layout.prop(operator, "export_animations", text="")
1241 def draw(self, context):
1242 layout = self.layout
1243 layout.use_property_split = True
1244 layout.use_property_decorate = False # No animation.
1246 sfile = context.space_data
1247 operator = sfile.active_operator
1249 layout.active = operator.export_animations
1251 layout.prop(operator, 'export_animation_mode')
1252 if operator.export_animation_mode == "ACTIVE_ACTIONS":
1253 layout.prop(operator, 'export_nla_strips_merged_animation_name')
1255 row = layout.row()
1256 row.active = operator.export_force_sampling and operator.export_animation_mode in ['ACTIONS', 'ACTIVE_ACTIONS']
1257 row.prop(operator, 'export_bake_animation')
1258 if operator.export_animation_mode == "SCENE":
1259 layout.prop(operator, 'export_anim_scene_split_object')
1261 class GLTF_PT_export_animation_notes(bpy.types.Panel):
1262 bl_space_type = 'FILE_BROWSER'
1263 bl_region_type = 'TOOL_PROPS'
1264 bl_label = "Notes"
1265 bl_parent_id = "GLTF_PT_export_animation"
1266 bl_options = {'DEFAULT_CLOSED'}
1268 @classmethod
1269 def poll(cls, context):
1270 sfile = context.space_data
1271 operator = sfile.active_operator
1273 return operator.bl_idname == "EXPORT_SCENE_OT_gltf" and \
1274 operator.export_animation_mode in ["NLA_TRACKS", "SCENE"]
1276 def draw(self, context):
1277 operator = context.space_data.active_operator
1278 layout = self.layout
1279 if operator.export_animation_mode == "SCENE":
1280 layout.label(text="Scene mode uses full bake mode:")
1281 layout.label(text="- sampling is active")
1282 layout.label(text="- baking all objects is active")
1283 layout.label(text="- Using scene frame range")
1284 elif operator.export_animation_mode == "NLA_TRACKS":
1285 layout.label(text="Track mode uses full bake mode:")
1286 layout.label(text="- sampling is active")
1287 layout.label(text="- baking all objects is active")
1289 class GLTF_PT_export_animation_ranges(bpy.types.Panel):
1290 bl_space_type = 'FILE_BROWSER'
1291 bl_region_type = 'TOOL_PROPS'
1292 bl_label = "Rest & Ranges"
1293 bl_parent_id = "GLTF_PT_export_animation"
1294 bl_options = {'DEFAULT_CLOSED'}
1296 @classmethod
1297 def poll(cls, context):
1298 sfile = context.space_data
1299 operator = sfile.active_operator
1301 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1303 def draw(self, context):
1304 layout = self.layout
1305 layout.use_property_split = True
1306 layout.use_property_decorate = False # No animation.
1308 sfile = context.space_data
1309 operator = sfile.active_operator
1311 layout.active = operator.export_animations
1313 layout.prop(operator, 'export_current_frame')
1314 row = layout.row()
1315 row.active = operator.export_animation_mode in ['ACTIONS', 'ACTIVE_ACTIONS', 'NLA_TRACKS']
1316 row.prop(operator, 'export_frame_range')
1317 layout.prop(operator, 'export_anim_slide_to_zero')
1318 row = layout.row()
1319 row.active = operator.export_animation_mode in ['ACTIONS', 'ACTIVE_ACTIONS', 'NLA_TRACKS']
1320 layout.prop(operator, 'export_negative_frame')
1322 class GLTF_PT_export_animation_armature(bpy.types.Panel):
1323 bl_space_type = 'FILE_BROWSER'
1324 bl_region_type = 'TOOL_PROPS'
1325 bl_label = "Armature"
1326 bl_parent_id = "GLTF_PT_export_animation"
1327 bl_options = {'DEFAULT_CLOSED'}
1329 @classmethod
1330 def poll(cls, context):
1331 sfile = context.space_data
1332 operator = sfile.active_operator
1334 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1336 def draw(self, context):
1337 layout = self.layout
1338 layout.use_property_split = True
1339 layout.use_property_decorate = False # No animation.
1341 sfile = context.space_data
1342 operator = sfile.active_operator
1344 layout.active = operator.export_animations
1346 layout.prop(operator, 'export_anim_single_armature')
1347 layout.prop(operator, 'export_reset_pose_bones')
1349 class GLTF_PT_export_animation_shapekeys(bpy.types.Panel):
1350 bl_space_type = 'FILE_BROWSER'
1351 bl_region_type = 'TOOL_PROPS'
1352 bl_label = "Shapekeys Animation"
1353 bl_parent_id = "GLTF_PT_export_animation"
1354 bl_options = {'DEFAULT_CLOSED'}
1356 @classmethod
1357 def poll(cls, context):
1358 sfile = context.space_data
1359 operator = sfile.active_operator
1361 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1363 def draw_header(self, context):
1364 sfile = context.space_data
1365 operator = sfile.active_operator
1366 self.layout.active = operator.export_animations and operator.export_morph
1367 self.layout.prop(operator, "export_morph_animation", text="")
1369 def draw(self, context):
1370 layout = self.layout
1371 layout.use_property_split = True
1372 layout.use_property_decorate = False # No animation.
1374 sfile = context.space_data
1375 operator = sfile.active_operator
1377 layout.active = operator.export_animations
1379 layout.prop(operator, 'export_morph_reset_sk_data')
1382 class GLTF_PT_export_animation_sampling(bpy.types.Panel):
1383 bl_space_type = 'FILE_BROWSER'
1384 bl_region_type = 'TOOL_PROPS'
1385 bl_label = "Sampling Animations"
1386 bl_parent_id = "GLTF_PT_export_animation"
1387 bl_options = {'DEFAULT_CLOSED'}
1389 @classmethod
1390 def poll(cls, context):
1391 sfile = context.space_data
1392 operator = sfile.active_operator
1394 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1396 def draw_header(self, context):
1397 sfile = context.space_data
1398 operator = sfile.active_operator
1399 self.layout.active = operator.export_animations and operator.export_animation_mode in ['ACTIONS', 'ACTIVE_ACTIONS']
1400 self.layout.prop(operator, "export_force_sampling", text="")
1402 def draw(self, context):
1403 layout = self.layout
1404 layout.use_property_split = True
1405 layout.use_property_decorate = False # No animation.
1407 sfile = context.space_data
1408 operator = sfile.active_operator
1410 layout.active = operator.export_animations
1412 layout.prop(operator, 'export_frame_step')
1415 class GLTF_PT_export_animation_optimize(bpy.types.Panel):
1416 bl_space_type = 'FILE_BROWSER'
1417 bl_region_type = 'TOOL_PROPS'
1418 bl_label = "Optimize Animations"
1419 bl_parent_id = "GLTF_PT_export_animation"
1420 bl_options = {'DEFAULT_CLOSED'}
1422 @classmethod
1423 def poll(cls, context):
1424 sfile = context.space_data
1425 operator = sfile.active_operator
1427 return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
1429 def draw(self, context):
1430 layout = self.layout
1431 layout.use_property_split = True
1432 layout.use_property_decorate = False # No animation.
1434 sfile = context.space_data
1435 operator = sfile.active_operator
1437 layout.active = operator.export_animations
1439 layout.prop(operator, 'export_optimize_animation_size')
1441 row = layout.row()
1442 row.prop(operator, 'export_optimize_animation_keep_anim_armature')
1444 row = layout.row()
1445 row.prop(operator, 'export_optimize_animation_keep_anim_object')
1448 class GLTF_PT_export_user_extensions(bpy.types.Panel):
1449 bl_space_type = 'FILE_BROWSER'
1450 bl_region_type = 'TOOL_PROPS'
1451 bl_label = "Exporter Extensions"
1452 bl_parent_id = "FILE_PT_operator"
1453 bl_options = {'DEFAULT_CLOSED'}
1455 @classmethod
1456 def poll(cls, context):
1457 sfile = context.space_data
1458 operator = sfile.active_operator
1460 return operator.bl_idname == "EXPORT_SCENE_OT_gltf" and operator.has_active_exporter_extensions
1462 def draw(self, context):
1463 layout = self.layout
1464 layout.use_property_split = True
1465 layout.use_property_decorate = False # No animation.
1467 class GLTF_PT_import_user_extensions(bpy.types.Panel):
1468 bl_space_type = 'FILE_BROWSER'
1469 bl_region_type = 'TOOL_PROPS'
1470 bl_label = "Importer Extensions"
1471 bl_parent_id = "FILE_PT_operator"
1472 bl_options = {'DEFAULT_CLOSED'}
1474 @classmethod
1475 def poll(cls, context):
1476 sfile = context.space_data
1477 operator = sfile.active_operator
1478 return operator.bl_idname == "IMPORT_SCENE_OT_gltf" and operator.has_active_importer_extensions
1480 def draw(self, context):
1481 layout = self.layout
1482 layout.use_property_split = True
1483 layout.use_property_decorate = False # No animation.
1485 class ExportGLTF2(bpy.types.Operator, ExportGLTF2_Base, ExportHelper):
1486 """Export scene as glTF 2.0 file"""
1487 bl_idname = 'export_scene.gltf'
1488 bl_label = 'Export glTF 2.0'
1490 filename_ext = ''
1492 filter_glob: StringProperty(default='*.glb', options={'HIDDEN'})
1495 def menu_func_export(self, context):
1496 self.layout.operator(ExportGLTF2.bl_idname, text='glTF 2.0 (.glb/.gltf)')
1499 class ImportGLTF2(Operator, ConvertGLTF2_Base, ImportHelper):
1500 """Load a glTF 2.0 file"""
1501 bl_idname = 'import_scene.gltf'
1502 bl_label = 'Import glTF 2.0'
1503 bl_options = {'REGISTER', 'UNDO'}
1505 filter_glob: StringProperty(default="*.glb;*.gltf", options={'HIDDEN'})
1507 files: CollectionProperty(
1508 name="File Path",
1509 type=bpy.types.OperatorFileListElement,
1512 loglevel: IntProperty(
1513 name='Log Level',
1514 description="Log Level")
1516 import_pack_images: BoolProperty(
1517 name='Pack Images',
1518 description='Pack all images into .blend file',
1519 default=True
1522 merge_vertices: BoolProperty(
1523 name='Merge Vertices',
1524 description=(
1525 'The glTF format requires discontinuous normals, UVs, and '
1526 'other vertex attributes to be stored as separate vertices, '
1527 'as required for rendering on typical graphics hardware. '
1528 'This option attempts to combine co-located vertices where possible. '
1529 'Currently cannot combine verts with different normals'
1531 default=False,
1534 import_shading: EnumProperty(
1535 name="Shading",
1536 items=(("NORMALS", "Use Normal Data", ""),
1537 ("FLAT", "Flat Shading", ""),
1538 ("SMOOTH", "Smooth Shading", "")),
1539 description="How normals are computed during import",
1540 default="NORMALS")
1542 bone_heuristic: EnumProperty(
1543 name="Bone Dir",
1544 items=(
1545 ("BLENDER", "Blender (best for re-importing)",
1546 "Good for re-importing glTFs exported from Blender. "
1547 "Bone tips are placed on their local +Y axis (in glTF space)"),
1548 ("TEMPERANCE", "Temperance (average)",
1549 "Decent all-around strategy. "
1550 "A bone with one child has its tip placed on the local axis "
1551 "closest to its child"),
1552 ("FORTUNE", "Fortune (may look better, less accurate)",
1553 "Might look better than Temperance, but also might have errors. "
1554 "A bone with one child has its tip placed at its child's root. "
1555 "Non-uniform scalings may get messed up though, so beware"),
1557 description="Heuristic for placing bones. Tries to make bones pretty",
1558 default="TEMPERANCE",
1561 guess_original_bind_pose: BoolProperty(
1562 name='Guess Original Bind Pose',
1563 description=(
1564 'Try to guess the original bind pose for skinned meshes from '
1565 'the inverse bind matrices. '
1566 'When off, use default/rest pose as bind pose'
1568 default=True,
1571 def draw(self, context):
1572 layout = self.layout
1574 layout.use_property_split = True
1575 layout.use_property_decorate = False # No animation.
1577 layout.prop(self, 'import_pack_images')
1578 layout.prop(self, 'merge_vertices')
1579 layout.prop(self, 'import_shading')
1580 layout.prop(self, 'guess_original_bind_pose')
1581 layout.prop(self, 'bone_heuristic')
1582 layout.prop(self, 'export_import_convert_lighting_mode')
1584 def invoke(self, context, event):
1585 import sys
1586 preferences = bpy.context.preferences
1587 for addon_name in preferences.addons.keys():
1588 try:
1589 if hasattr(sys.modules[addon_name], 'glTF2ImportUserExtension') or hasattr(sys.modules[addon_name], 'glTF2ImportUserExtensions'):
1590 importer_extension_panel_unregister_functors.append(sys.modules[addon_name].register_panel())
1591 except Exception:
1592 pass
1594 self.has_active_importer_extensions = len(importer_extension_panel_unregister_functors) > 0
1595 return ImportHelper.invoke(self, context, event)
1597 def execute(self, context):
1598 return self.import_gltf2(context)
1600 def import_gltf2(self, context):
1601 import os
1603 self.set_debug_log()
1604 import_settings = self.as_keywords()
1606 user_extensions = []
1608 import sys
1609 preferences = bpy.context.preferences
1610 for addon_name in preferences.addons.keys():
1611 try:
1612 module = sys.modules[addon_name]
1613 except Exception:
1614 continue
1615 if hasattr(module, 'glTF2ImportUserExtension'):
1616 extension_ctor = module.glTF2ImportUserExtension
1617 user_extensions.append(extension_ctor())
1618 import_settings['import_user_extensions'] = user_extensions
1620 if self.files:
1621 # Multiple file import
1622 ret = {'CANCELLED'}
1623 dirname = os.path.dirname(self.filepath)
1624 for file in self.files:
1625 path = os.path.join(dirname, file.name)
1626 if self.unit_import(path, import_settings) == {'FINISHED'}:
1627 ret = {'FINISHED'}
1628 return ret
1629 else:
1630 # Single file import
1631 return self.unit_import(self.filepath, import_settings)
1633 def unit_import(self, filename, import_settings):
1634 import time
1635 from .io.imp.gltf2_io_gltf import glTFImporter, ImportError
1636 from .blender.imp.gltf2_blender_gltf import BlenderGlTF
1638 try:
1639 gltf_importer = glTFImporter(filename, import_settings)
1640 gltf_importer.read()
1641 gltf_importer.checks()
1643 print("Data are loaded, start creating Blender stuff")
1645 start_time = time.time()
1646 BlenderGlTF.create(gltf_importer)
1647 elapsed_s = "{:.2f}s".format(time.time() - start_time)
1648 print("glTF import finished in " + elapsed_s)
1650 gltf_importer.log.removeHandler(gltf_importer.log_handler)
1652 return {'FINISHED'}
1654 except ImportError as e:
1655 self.report({'ERROR'}, e.args[0])
1656 return {'CANCELLED'}
1658 def set_debug_log(self):
1659 import logging
1660 if bpy.app.debug_value == 0:
1661 self.loglevel = logging.CRITICAL
1662 elif bpy.app.debug_value == 1:
1663 self.loglevel = logging.ERROR
1664 elif bpy.app.debug_value == 2:
1665 self.loglevel = logging.WARNING
1666 elif bpy.app.debug_value == 3:
1667 self.loglevel = logging.INFO
1668 else:
1669 self.loglevel = logging.NOTSET
1672 def gltf_variant_ui_update(self, context):
1673 from .blender.com.gltf2_blender_ui import variant_register, variant_unregister
1674 if self.KHR_materials_variants_ui is True:
1675 # register all needed types
1676 variant_register()
1677 else:
1678 variant_unregister()
1680 def gltf_animation_ui_update(self, context):
1681 from .blender.com.gltf2_blender_ui import anim_ui_register, anim_ui_unregister
1682 if self.animation_ui is True:
1683 # register all needed types
1684 anim_ui_register()
1685 else:
1686 anim_ui_unregister()
1688 class GLTF_AddonPreferences(bpy.types.AddonPreferences):
1689 bl_idname = __package__
1691 settings_node_ui : bpy.props.BoolProperty(
1692 default= False,
1693 description="Displays glTF Material Output node in Shader Editor (Menu Add > Output)"
1696 KHR_materials_variants_ui : bpy.props.BoolProperty(
1697 default= False,
1698 description="Displays glTF UI to manage material variants",
1699 update=gltf_variant_ui_update
1702 animation_ui: bpy.props.BoolProperty(
1703 default=False,
1704 description="Display glTF UI to manage animations",
1705 update=gltf_animation_ui_update
1708 def draw(self, context):
1709 layout = self.layout
1710 row = layout.row()
1711 row.prop(self, "settings_node_ui", text="Shader Editor Add-ons")
1712 row.prop(self, "KHR_materials_variants_ui", text="Material Variants")
1713 row.prop(self, "animation_ui", text="Animation UI")
1715 def menu_func_import(self, context):
1716 self.layout.operator(ImportGLTF2.bl_idname, text='glTF 2.0 (.glb/.gltf)')
1719 classes = (
1720 ExportGLTF2,
1721 GLTF_PT_export_main,
1722 GLTF_PT_export_include,
1723 GLTF_PT_export_transform,
1724 GLTF_PT_export_data,
1725 GLTF_PT_export_data_mesh,
1726 GLTF_PT_export_data_material,
1727 GLTF_PT_export_data_original_pbr,
1728 GLTF_PT_export_data_shapekeys,
1729 GLTF_PT_export_data_armature,
1730 GLTF_PT_export_data_skinning,
1731 GLTF_PT_export_data_lighting,
1732 GLTF_PT_export_data_compression,
1733 GLTF_PT_export_animation,
1734 GLTF_PT_export_animation_notes,
1735 GLTF_PT_export_animation_ranges,
1736 GLTF_PT_export_animation_armature,
1737 GLTF_PT_export_animation_shapekeys,
1738 GLTF_PT_export_animation_sampling,
1739 GLTF_PT_export_animation_optimize,
1740 GLTF_PT_export_user_extensions,
1741 ImportGLTF2,
1742 GLTF_PT_import_user_extensions,
1743 GLTF_AddonPreferences
1747 def register():
1748 from .blender.com import gltf2_blender_ui as blender_ui
1749 for c in classes:
1750 bpy.utils.register_class(c)
1751 # bpy.utils.register_module(__name__)
1753 blender_ui.register()
1754 if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True:
1755 blender_ui.variant_register()
1756 if bpy.context.preferences.addons['io_scene_gltf2'].preferences.animation_ui is True:
1757 blender_ui.anim_ui_register()
1759 # add to the export / import menu
1760 bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
1761 bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
1764 def unregister():
1765 from .blender.com import gltf2_blender_ui as blender_ui
1766 blender_ui.unregister()
1767 if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True:
1768 blender_ui.variant_unregister()
1770 for c in classes:
1771 bpy.utils.unregister_class(c)
1772 for f in exporter_extension_panel_unregister_functors:
1774 exporter_extension_panel_unregister_functors.clear()
1776 for f in importer_extension_panel_unregister_functors:
1778 importer_extension_panel_unregister_functors.clear()
1780 # bpy.utils.unregister_module(__name__)
1782 # remove from the export / import menu
1783 bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
1784 bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)