Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Duroure <julien.duroure@gmail.com>2020-05-12 19:37:27 +0300
committerJulien Duroure <julien.duroure@gmail.com>2020-05-12 19:37:27 +0300
commit93aea3746d4136854fe74c15a0b6ebd094bbf692 (patch)
tree38245cb569f5db86cb164ae998a434c3b019b532
parent77f99e2553ead0fa7f31f8f5b28183e525427308 (diff)
glTF: fix bad ending line again
-rwxr-xr-xio_scene_gltf2/__init__.py2012
1 files changed, 1006 insertions, 1006 deletions
diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py
index 9f29af9e..09f5428b 100755
--- a/io_scene_gltf2/__init__.py
+++ b/io_scene_gltf2/__init__.py
@@ -1,1006 +1,1006 @@
-# Copyright 2018-2019 The glTF-Blender-IO authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-bl_info = {
- 'name': 'glTF 2.0 format',
- 'author': 'Julien Duroure, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
- "version": (1, 3, 13),
- 'blender': (2, 90, 0),
- 'location': 'File > Import-Export',
- 'description': 'Import-Export as glTF 2.0',
- 'warning': '',
- 'doc_url': "{BLENDER_MANUAL_URL}/addons/import_export/scene_gltf2.html",
- 'tracker_url': "https://github.com/KhronosGroup/glTF-Blender-IO/issues/",
- 'support': 'OFFICIAL',
- 'category': 'Import-Export',
-}
-
-def get_version_string():
- return str(bl_info['version'][0]) + '.' + str(bl_info['version'][1]) + '.' + str(bl_info['version'][2])
-
-#
-# Script reloading (if the user calls 'Reload Scripts' from Blender)
-#
-
-def reload_package(module_dict_main):
- import importlib
- from pathlib import Path
-
- def reload_package_recursive(current_dir, module_dict):
- for path in current_dir.iterdir():
- if "__init__" in str(path) or path.stem not in module_dict:
- continue
-
- if path.is_file() and path.suffix == ".py":
- importlib.reload(module_dict[path.stem])
- elif path.is_dir():
- reload_package_recursive(path, module_dict[path.stem].__dict__)
-
- reload_package_recursive(Path(__file__).parent, module_dict_main)
-
-
-if "bpy" in locals():
- reload_package(locals())
-
-import bpy
-from bpy.props import (StringProperty,
- BoolProperty,
- EnumProperty,
- IntProperty,
- CollectionProperty)
-from bpy.types import Operator
-from bpy_extras.io_utils import ImportHelper, ExportHelper
-
-
-#
-# Functions / Classes.
-#
-
-extension_panel_unregister_functors = []
-
-class ExportGLTF2_Base:
- # TODO: refactor to avoid boilerplate
-
- def __init__(self):
- from io_scene_gltf2.io.exp import gltf2_io_draco_compression_extension
- self.is_draco_available = gltf2_io_draco_compression_extension.dll_exists()
-
- bl_options = {'PRESET'}
-
- export_format: EnumProperty(
- name='Format',
- items=(('GLB', 'glTF Binary (.glb)',
- 'Exports a single file, with all data packed in binary form. '
- 'Most efficient and portable, but more difficult to edit later'),
- ('GLTF_EMBEDDED', 'glTF Embedded (.gltf)',
- 'Exports a single file, with all data packed in JSON. '
- 'Less efficient than binary, but easier to edit later'),
- ('GLTF_SEPARATE', 'glTF Separate (.gltf + .bin + textures)',
- 'Exports multiple files, with separate JSON, binary and texture data. '
- 'Easiest to edit later')),
- description=(
- 'Output format and embedding options. Binary is most efficient, '
- 'but JSON (embedded or separate) may be easier to edit later'
- ),
- default='GLB'
- )
-
- ui_tab: EnumProperty(
- items=(('GENERAL', "General", "General settings"),
- ('MESHES', "Meshes", "Mesh settings"),
- ('OBJECTS', "Objects", "Object settings"),
- ('ANIMATION', "Animation", "Animation settings")),
- name="ui_tab",
- description="Export setting categories",
- )
-
- export_copyright: StringProperty(
- name='Copyright',
- description='Legal rights and conditions for the model',
- default=''
- )
-
- export_image_format: EnumProperty(
- name='Images',
- items=(('AUTO', 'Automatic',
- 'Save PNGs as PNGs and JPEGs as JPEGs.\n'
- 'If neither one, use PNG'),
- ('JPEG', 'JPEG Format (.jpg)',
- 'Save images as JPEGs. (Images that need alpha are saved as PNGs though.)\n'
- 'Be aware of a possible loss in quality'),
- ),
- description=(
- 'Output format for images. PNG is lossless and generally preferred, but JPEG might be preferable for web '
- 'applications due to the smaller file size'
- ),
- default='AUTO'
- )
-
- export_texture_dir: StringProperty(
- name='Textures',
- description='Folder to place texture files in. Relative to the .gltf file',
- default='',
- )
-
- export_texcoords: BoolProperty(
- name='UVs',
- description='Export UVs (texture coordinates) with meshes',
- default=True
- )
-
- export_normals: BoolProperty(
- name='Normals',
- description='Export vertex normals with meshes',
- default=True
- )
-
- export_draco_mesh_compression_enable: BoolProperty(
- name='Draco mesh compression',
- description='Compress mesh using Draco',
- default=False
- )
-
- export_draco_mesh_compression_level: IntProperty(
- name='Compression level',
- description='Compression level (0 = most speed, 6 = most compression, higher values currently not supported)',
- default=6,
- min=0,
- max=6
- )
-
- export_draco_position_quantization: IntProperty(
- name='Position quantization bits',
- description='Quantization bits for position values (0 = no quantization)',
- default=14,
- min=0,
- max=30
- )
-
- export_draco_normal_quantization: IntProperty(
- name='Normal quantization bits',
- description='Quantization bits for normal values (0 = no quantization)',
- default=10,
- min=0,
- max=30
- )
-
- export_draco_texcoord_quantization: IntProperty(
- name='Texcoord quantization bits',
- description='Quantization bits for texture coordinate values (0 = no quantization)',
- default=12,
- min=0,
- max=30
- )
-
- export_draco_generic_quantization: IntProperty(
- name='Generic quantization bits',
- description='Quantization bits for generic coordinate values like weights or joints (0 = no quantization)',
- default=12,
- min=0,
- max=30
- )
-
- export_tangents: BoolProperty(
- name='Tangents',
- description='Export vertex tangents with meshes',
- default=False
- )
-
- export_materials: BoolProperty(
- name='Materials',
- description='Export materials',
- default=True
- )
-
- export_colors: BoolProperty(
- name='Vertex Colors',
- description='Export vertex colors with meshes',
- default=True
- )
-
- export_cameras: BoolProperty(
- name='Cameras',
- description='Export cameras',
- default=False
- )
-
- # keep it for compatibility (for now)
- export_selected: BoolProperty(
- name='Selected Objects',
- description='Export selected objects only',
- default=False
- )
-
- use_selection: BoolProperty(
- name='Selected Objects',
- description='Export selected objects only',
- default=False
- )
-
- export_extras: BoolProperty(
- name='Custom Properties',
- description='Export custom properties as glTF extras',
- default=False
- )
-
- export_yup: BoolProperty(
- name='+Y Up',
- description='Export using glTF convention, +Y up',
- default=True
- )
-
- export_apply: BoolProperty(
- name='Apply Modifiers',
- description='Apply modifiers (excluding Armatures) to mesh objects -'
- 'WARNING: prevents exporting shape keys',
- default=False
- )
-
- export_animations: BoolProperty(
- name='Animations',
- description='Exports active actions and NLA tracks as glTF animations',
- default=True
- )
-
- export_frame_range: BoolProperty(
- name='Limit to Playback Range',
- description='Clips animations to selected playback range',
- default=True
- )
-
- export_frame_step: IntProperty(
- name='Sampling Rate',
- description='How often to evaluate animated values (in frames)',
- default=1,
- min=1,
- max=120
- )
-
- export_force_sampling: BoolProperty(
- name='Always Sample Animations',
- description='Apply sampling to all animations',
- default=True
- )
-
- export_nla_strips: BoolProperty(
- name='Group by NLA Track',
- description=(
- "When on, multiple actions become part of the same glTF animation if\n"
- "they're pushed onto NLA tracks with the same name.\n"
- "When off, all the currently assigned actions become one glTF animation"
- ),
- default=True
- )
-
- export_def_bones: BoolProperty(
- name='Export Deformation Bones Only',
- description='Export Deformation bones only (and needed bones for hierarchy)',
- default=False
- )
-
- export_current_frame: BoolProperty(
- name='Use Current Frame',
- description='Export the scene in the current animation frame',
- default=False
- )
-
- export_skins: BoolProperty(
- name='Skinning',
- description='Export skinning (armature) data',
- default=True
- )
-
- export_all_influences: BoolProperty(
- name='Include All Bone Influences',
- description='Allow >4 joint vertex influences. Models may appear incorrectly in many viewers',
- default=False
- )
-
- export_morph: BoolProperty(
- name='Shape Keys',
- description='Export shape keys (morph targets)',
- default=True
- )
-
- export_morph_normal: BoolProperty(
- name='Shape Key Normals',
- description='Export vertex normals with shape keys (morph targets)',
- default=True
- )
-
- export_morph_tangent: BoolProperty(
- name='Shape Key Tangents',
- description='Export vertex tangents with shape keys (morph targets)',
- default=False
- )
-
- export_lights: BoolProperty(
- name='Punctual Lights',
- description='Export directional, point, and spot lights. '
- 'Uses "KHR_lights_punctual" glTF extension',
- default=False
- )
-
- export_displacement: BoolProperty(
- name='Displacement Textures (EXPERIMENTAL)',
- description='EXPERIMENTAL: Export displacement textures. '
- 'Uses incomplete "KHR_materials_displacement" glTF extension',
- default=False
- )
-
- will_save_settings: BoolProperty(
- name='Remember Export Settings',
- description='Store glTF export settings in the Blender project',
- default=False)
-
- # Custom scene property for saving settings
- scene_key = "glTF2ExportSettings"
-
- #
-
- def invoke(self, context, event):
- settings = context.scene.get(self.scene_key)
- self.will_save_settings = False
- self.has_active_extenions = False
- if settings:
- try:
- for (k, v) in settings.items():
- if k == "export_selected": # Back compatibility for export_selected --> use_selection
- setattr(self, "use_selection", v)
- del settings[k]
- settings["use_selection"] = v
- print("export_selected is now renamed use_selection, and will be deleted in a few release")
- else:
- setattr(self, k, v)
- self.will_save_settings = True
-
- except (AttributeError, TypeError):
- self.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings")
- del context.scene[self.scene_key]
-
- import sys
- preferences = bpy.context.preferences
- for addon_name in preferences.addons.keys():
- try:
- if hasattr(sys.modules[addon_name], 'glTF2ExportUserExtension') or hasattr(sys.modules[addon_name], 'glTF2ExportUserExtensions'):
- extension_panel_unregister_functors.append(sys.modules[addon_name].register_panel())
- self.has_active_extenions = True
- except Exception:
- pass
-
- return ExportHelper.invoke(self, context, event)
-
- def save_settings(self, context):
- # find all export_ props
- all_props = self.properties
- export_props = {x: getattr(self, x) for x in dir(all_props)
- if x.startswith("export_") and all_props.get(x) is not None}
-
- context.scene[self.scene_key] = export_props
-
- def execute(self, context):
- import os
- import datetime
- from .blender.exp import gltf2_blender_export
-
- if self.will_save_settings:
- self.save_settings(context)
-
- if self.export_format == 'GLB':
- self.filename_ext = '.glb'
- else:
- self.filename_ext = '.gltf'
-
- # All custom export settings are stored in this container.
- export_settings = {}
-
- export_settings['timestamp'] = datetime.datetime.now()
-
- export_settings['gltf_filepath'] = bpy.path.ensure_ext(self.filepath, self.filename_ext)
- export_settings['gltf_filedirectory'] = os.path.dirname(export_settings['gltf_filepath']) + '/'
- export_settings['gltf_texturedirectory'] = os.path.join(
- export_settings['gltf_filedirectory'],
- self.export_texture_dir,
- )
-
- export_settings['gltf_format'] = self.export_format
- export_settings['gltf_image_format'] = self.export_image_format
- export_settings['gltf_copyright'] = self.export_copyright
- export_settings['gltf_texcoords'] = self.export_texcoords
- export_settings['gltf_normals'] = self.export_normals
- export_settings['gltf_tangents'] = self.export_tangents and self.export_normals
-
- if self.is_draco_available:
- export_settings['gltf_draco_mesh_compression'] = self.export_draco_mesh_compression_enable
- export_settings['gltf_draco_mesh_compression_level'] = self.export_draco_mesh_compression_level
- export_settings['gltf_draco_position_quantization'] = self.export_draco_position_quantization
- export_settings['gltf_draco_normal_quantization'] = self.export_draco_normal_quantization
- export_settings['gltf_draco_texcoord_quantization'] = self.export_draco_texcoord_quantization
- export_settings['gltf_draco_generic_quantization'] = self.export_draco_generic_quantization
- else:
- export_settings['gltf_draco_mesh_compression'] = False
-
- export_settings['gltf_materials'] = self.export_materials
- export_settings['gltf_colors'] = self.export_colors
- export_settings['gltf_cameras'] = self.export_cameras
-
- # compatibility after renaming export_selected to use_selection
- if self.export_selected is True:
- self.report({"WARNING"}, "export_selected is now renamed use_selection, and will be deleted in a few release")
- export_settings['gltf_selected'] = self.export_selected
- else:
- export_settings['gltf_selected'] = self.use_selection
-
- # export_settings['gltf_selected'] = self.use_selection This can be uncomment when removing compatibility of export_selected
- export_settings['gltf_layers'] = True # self.export_layers
- export_settings['gltf_extras'] = self.export_extras
- export_settings['gltf_yup'] = self.export_yup
- export_settings['gltf_apply'] = self.export_apply
- export_settings['gltf_current_frame'] = self.export_current_frame
- export_settings['gltf_animations'] = self.export_animations
- if self.export_animations:
- export_settings['gltf_frame_range'] = self.export_frame_range
- export_settings['gltf_force_sampling'] = self.export_force_sampling
- if self.export_force_sampling:
- export_settings['gltf_def_bones'] = self.export_def_bones
- else:
- export_settings['gltf_def_bones'] = False
- export_settings['gltf_nla_strips'] = self.export_nla_strips
- else:
- export_settings['gltf_frame_range'] = False
- export_settings['gltf_move_keyframes'] = False
- export_settings['gltf_force_sampling'] = False
- export_settings['gltf_def_bones'] = False
- export_settings['gltf_skins'] = self.export_skins
- if self.export_skins:
- export_settings['gltf_all_vertex_influences'] = self.export_all_influences
- else:
- export_settings['gltf_all_vertex_influences'] = False
- export_settings['gltf_frame_step'] = self.export_frame_step
- export_settings['gltf_morph'] = self.export_morph
- if self.export_morph:
- export_settings['gltf_morph_normal'] = self.export_morph_normal
- else:
- export_settings['gltf_morph_normal'] = False
- if self.export_morph and self.export_morph_normal:
- export_settings['gltf_morph_tangent'] = self.export_morph_tangent
- else:
- export_settings['gltf_morph_tangent'] = False
-
- export_settings['gltf_lights'] = self.export_lights
- export_settings['gltf_displacement'] = self.export_displacement
-
- export_settings['gltf_binary'] = bytearray()
- export_settings['gltf_binaryfilename'] = os.path.splitext(os.path.basename(
- bpy.path.ensure_ext(self.filepath,self.filename_ext)))[0] + '.bin'
-
- user_extensions = []
-
- import sys
- preferences = bpy.context.preferences
- for addon_name in preferences.addons.keys():
- try:
- module = sys.modules[addon_name]
- except Exception:
- continue
- if hasattr(module, 'glTF2ExportUserExtension'):
- extension_ctor = module.glTF2ExportUserExtension
- user_extensions.append(extension_ctor())
- if hasattr(module, 'glTF2ExportUserExtensions'):
- extension_ctors = module.glTF2ExportUserExtensions
- for extension_ctor in extension_ctors:
- user_extensions.append(extension_ctor())
- export_settings['gltf_user_extensions'] = user_extensions
-
- return gltf2_blender_export.save(context, export_settings)
-
- def draw(self, context):
- pass # Is needed to get panels available
-
-
-class GLTF_PT_export_main(bpy.types.Panel):
- bl_space_type = 'FILE_BROWSER'
- bl_region_type = 'TOOL_PROPS'
- bl_label = ""
- bl_parent_id = "FILE_PT_operator"
- bl_options = {'HIDE_HEADER'}
-
- @classmethod
- def poll(cls, context):
- sfile = context.space_data
- operator = sfile.active_operator
-
- return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- layout.use_property_decorate = False # No animation.
-
- sfile = context.space_data
- operator = sfile.active_operator
-
- layout.prop(operator, 'export_format')
- if operator.export_format == 'GLTF_SEPARATE':
- layout.prop(operator, 'export_texture_dir', icon='FILE_FOLDER')
- layout.prop(operator, 'export_copyright')
- layout.prop(operator, 'will_save_settings')
-
-
-class GLTF_PT_export_include(bpy.types.Panel):
- bl_space_type = 'FILE_BROWSER'
- bl_region_type = 'TOOL_PROPS'
- bl_label = "Include"
- bl_parent_id = "FILE_PT_operator"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- sfile = context.space_data
- operator = sfile.active_operator
-
- return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- layout.use_property_decorate = False # No animation.
-
- sfile = context.space_data
- operator = sfile.active_operator
-
- col = layout.column(heading = "Limit to", align = True)
- col.prop(operator, 'use_selection')
-
- col = layout.column(heading = "Data", align = True)
- col.prop(operator, 'export_extras')
- col.prop(operator, 'export_cameras')
- col.prop(operator, 'export_lights')
-
-
-class GLTF_PT_export_transform(bpy.types.Panel):
- bl_space_type = 'FILE_BROWSER'
- bl_region_type = 'TOOL_PROPS'
- bl_label = "Transform"
- bl_parent_id = "FILE_PT_operator"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- sfile = context.space_data
- operator = sfile.active_operator
-
- return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- layout.use_property_decorate = False # No animation.
-
- sfile = context.space_data
- operator = sfile.active_operator
-
- layout.prop(operator, 'export_yup')
-
-
-class GLTF_PT_export_geometry(bpy.types.Panel):
- bl_space_type = 'FILE_BROWSER'
- bl_region_type = 'TOOL_PROPS'
- bl_label = "Geometry"
- bl_parent_id = "FILE_PT_operator"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- sfile = context.space_data
- operator = sfile.active_operator
-
- return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- layout.use_property_decorate = False # No animation.
-
- sfile = context.space_data
- operator = sfile.active_operator
-
- layout.prop(operator, 'export_apply')
- layout.prop(operator, 'export_texcoords')
- layout.prop(operator, 'export_normals')
- col = layout.column()
- col.active = operator.export_normals
- col.prop(operator, 'export_tangents')
- layout.prop(operator, 'export_colors')
- layout.prop(operator, 'export_materials')
- col = layout.column()
- col.active = operator.export_materials
- col.prop(operator, 'export_image_format')
-
-
-class GLTF_PT_export_geometry_compression(bpy.types.Panel):
- bl_space_type = 'FILE_BROWSER'
- bl_region_type = 'TOOL_PROPS'
- bl_label = "Compression"
- bl_parent_id = "GLTF_PT_export_geometry"
- bl_options = {'DEFAULT_CLOSED'}
-
- def __init__(self):
- from io_scene_gltf2.io.exp import gltf2_io_draco_compression_extension
- self.is_draco_available = gltf2_io_draco_compression_extension.dll_exists(quiet=True)
-
- @classmethod
- def poll(cls, context):
- sfile = context.space_data
- operator = sfile.active_operator
- if operator.is_draco_available:
- return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
-
- def draw_header(self, context):
- sfile = context.space_data
- operator = sfile.active_operator
- self.layout.prop(operator, "export_draco_mesh_compression_enable", text="")
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- layout.use_property_decorate = False # No animation.
-
- sfile = context.space_data
- operator = sfile.active_operator
-
- layout.active = operator.export_draco_mesh_compression_enable
- layout.prop(operator, 'export_draco_mesh_compression_level')
-
- col = layout.column(align=True)
- col.prop(operator, 'export_draco_position_quantization', text="Quantize Position")
- col.prop(operator, 'export_draco_normal_quantization', text="Normal")
- col.prop(operator, 'export_draco_texcoord_quantization', text="Tex Coords")
- col.prop(operator, 'export_draco_generic_quantization', text="Generic")
-
-
-class GLTF_PT_export_animation(bpy.types.Panel):
- bl_space_type = 'FILE_BROWSER'
- bl_region_type = 'TOOL_PROPS'
- bl_label = "Animation"
- bl_parent_id = "FILE_PT_operator"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- sfile = context.space_data
- operator = sfile.active_operator
-
- return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- layout.use_property_decorate = False # No animation.
-
- sfile = context.space_data
- operator = sfile.active_operator
-
- layout.prop(operator, 'export_current_frame')
-
-
-class GLTF_PT_export_animation_export(bpy.types.Panel):
- bl_space_type = 'FILE_BROWSER'
- bl_region_type = 'TOOL_PROPS'
- bl_label = "Animation"
- bl_parent_id = "GLTF_PT_export_animation"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- sfile = context.space_data
- operator = sfile.active_operator
-
- return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
-
- def draw_header(self, context):
- sfile = context.space_data
- operator = sfile.active_operator
- self.layout.prop(operator, "export_animations", text="")
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- layout.use_property_decorate = False # No animation.
-
- sfile = context.space_data
- operator = sfile.active_operator
-
- layout.active = operator.export_animations
-
- layout.prop(operator, 'export_frame_range')
- layout.prop(operator, 'export_frame_step')
- layout.prop(operator, 'export_force_sampling')
- layout.prop(operator, 'export_nla_strips')
-
- row = layout.row()
- row.active = operator.export_force_sampling
- row.prop(operator, 'export_def_bones')
-
-
-class GLTF_PT_export_animation_shapekeys(bpy.types.Panel):
- bl_space_type = 'FILE_BROWSER'
- bl_region_type = 'TOOL_PROPS'
- bl_label = "Shape Keys"
- bl_parent_id = "GLTF_PT_export_animation"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- sfile = context.space_data
- operator = sfile.active_operator
-
- return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
-
- def draw_header(self, context):
- sfile = context.space_data
- operator = sfile.active_operator
- self.layout.prop(operator, "export_morph", text="")
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- layout.use_property_decorate = False # No animation.
-
- sfile = context.space_data
- operator = sfile.active_operator
-
- layout.active = operator.export_morph
-
- layout.prop(operator, 'export_morph_normal')
- col = layout.column()
- col.active = operator.export_morph_normal
- col.prop(operator, 'export_morph_tangent')
-
-
-class GLTF_PT_export_animation_skinning(bpy.types.Panel):
- bl_space_type = 'FILE_BROWSER'
- bl_region_type = 'TOOL_PROPS'
- bl_label = "Skinning"
- bl_parent_id = "GLTF_PT_export_animation"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- sfile = context.space_data
- operator = sfile.active_operator
-
- return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
-
- def draw_header(self, context):
- sfile = context.space_data
- operator = sfile.active_operator
- self.layout.prop(operator, "export_skins", text="")
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- layout.use_property_decorate = False # No animation.
-
- sfile = context.space_data
- operator = sfile.active_operator
-
- layout.active = operator.export_skins
- layout.prop(operator, 'export_all_influences')
-
-class GLTF_PT_export_user_extensions(bpy.types.Panel):
- bl_space_type = 'FILE_BROWSER'
- bl_region_type = 'TOOL_PROPS'
- bl_label = "Extensions"
- bl_parent_id = "FILE_PT_operator"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- sfile = context.space_data
- operator = sfile.active_operator
-
- return operator.bl_idname == "EXPORT_SCENE_OT_gltf" and operator.has_active_extenions
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- layout.use_property_decorate = False # No animation.
-
-
-class ExportGLTF2(bpy.types.Operator, ExportGLTF2_Base, ExportHelper):
- """Export scene as glTF 2.0 file"""
- bl_idname = 'export_scene.gltf'
- bl_label = 'Export glTF 2.0'
-
- filename_ext = ''
-
- filter_glob: StringProperty(default='*.glb;*.gltf', options={'HIDDEN'})
-
-
-def menu_func_export(self, context):
- self.layout.operator(ExportGLTF2.bl_idname, text='glTF 2.0 (.glb/.gltf)')
-
-
-class ImportGLTF2(Operator, ImportHelper):
- """Load a glTF 2.0 file"""
- bl_idname = 'import_scene.gltf'
- bl_label = 'Import glTF 2.0'
-
- filter_glob: StringProperty(default="*.glb;*.gltf", options={'HIDDEN'})
-
- files: CollectionProperty(
- name="File Path",
- type=bpy.types.OperatorFileListElement,
- )
-
- loglevel: IntProperty(
- name='Log Level',
- description="Log Level")
-
- import_pack_images: BoolProperty(
- name='Pack Images',
- description='Pack all images into .blend file',
- default=True
- )
-
- import_shading: EnumProperty(
- name="Shading",
- items=(("NORMALS", "Use Normal Data", ""),
- ("FLAT", "Flat Shading", ""),
- ("SMOOTH", "Smooth Shading", "")),
- description="How normals are computed during import",
- default="NORMALS")
-
- bone_heuristic: EnumProperty(
- name="Bone Dir",
- items=(
- ("BLENDER", "Blender (best for re-importing)",
- "Good for re-importing glTFs exported from Blender.\n"
- "Bone tips are placed on their local +Y axis (in glTF space)"),
- ("TEMPERANCE", "Temperance (average)",
- "Decent all-around strategy.\n"
- "A bone with one child has its tip placed on the local axis\n"
- "closest to its child"),
- ("FORTUNE", "Fortune (may look better, less accurate)",
- "Might look better than Temperance, but also might have errors.\n"
- "A bone with one child has its tip placed at its child's root.\n"
- "Non-uniform scalings may get messed up though, so beware"),
- ),
- description="Heuristic for placing bones. Tries to make bones pretty",
- default="TEMPERANCE",
- )
-
- guess_original_bind_pose: BoolProperty(
- name='Guess Original Bind Pose',
- description=(
- 'Try to guess the original bind pose for skinned meshes from '
- 'the inverse bind matrices.\n'
- 'When off, use default/rest pose as bind pose'
- ),
- default=True,
- )
-
- def draw(self, context):
- layout = self.layout
-
- layout.use_property_split = True
- layout.use_property_decorate = False # No animation.
-
- layout.prop(self, 'import_pack_images')
- layout.prop(self, 'import_shading')
- layout.prop(self, 'guess_original_bind_pose')
- layout.prop(self, 'bone_heuristic')
-
- def execute(self, context):
- return self.import_gltf2(context)
-
- def import_gltf2(self, context):
- import os
-
- self.set_debug_log()
- import_settings = self.as_keywords()
-
- if self.files:
- # Multiple file import
- ret = {'CANCELLED'}
- dirname = os.path.dirname(self.filepath)
- for file in self.files:
- path = os.path.join(dirname, file.name)
- if self.unit_import(path, import_settings) == {'FINISHED'}:
- ret = {'FINISHED'}
- return ret
- else:
- # Single file import
- return self.unit_import(self.filepath, import_settings)
-
- def unit_import(self, filename, import_settings):
- import time
- from .io.imp.gltf2_io_gltf import glTFImporter
- from .blender.imp.gltf2_blender_gltf import BlenderGlTF
-
- self.gltf_importer = glTFImporter(filename, import_settings)
- success, txt = self.gltf_importer.read()
- if not success:
- self.report({'ERROR'}, txt)
- return {'CANCELLED'}
- success, txt = self.gltf_importer.checks()
- if not success:
- self.report({'ERROR'}, txt)
- return {'CANCELLED'}
- print("Data are loaded, start creating Blender stuff")
- start_time = time.time()
- BlenderGlTF.create(self.gltf_importer)
- elapsed_s = "{:.2f}s".format(time.time() - start_time)
- print("glTF import finished in " + elapsed_s)
- self.gltf_importer.log.removeHandler(self.gltf_importer.log_handler)
-
- return {'FINISHED'}
-
- def set_debug_log(self):
- import logging
- if bpy.app.debug_value == 0:
- self.loglevel = logging.CRITICAL
- elif bpy.app.debug_value == 1:
- self.loglevel = logging.ERROR
- elif bpy.app.debug_value == 2:
- self.loglevel = logging.WARNING
- elif bpy.app.debug_value == 3:
- self.loglevel = logging.INFO
- else:
- self.loglevel = logging.NOTSET
-
-
-def menu_func_import(self, context):
- self.layout.operator(ImportGLTF2.bl_idname, text='glTF 2.0 (.glb/.gltf)')
-
-
-classes = (
- ExportGLTF2,
- GLTF_PT_export_main,
- GLTF_PT_export_include,
- GLTF_PT_export_transform,
- GLTF_PT_export_geometry,
- GLTF_PT_export_geometry_compression,
- GLTF_PT_export_animation,
- GLTF_PT_export_animation_export,
- GLTF_PT_export_animation_shapekeys,
- GLTF_PT_export_animation_skinning,
- GLTF_PT_export_user_extensions,
- ImportGLTF2
-)
-
-
-def register():
- for c in classes:
- bpy.utils.register_class(c)
- # bpy.utils.register_module(__name__)
-
- # add to the export / import menu
- bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
- bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
-
-
-def unregister():
- for c in classes:
- bpy.utils.unregister_class(c)
- for f in extension_panel_unregister_functors:
- f()
- extension_panel_unregister_functors.clear()
-
- # bpy.utils.unregister_module(__name__)
-
- # remove from the export / import menu
- bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
- bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
+# Copyright 2018-2019 The glTF-Blender-IO authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+bl_info = {
+ 'name': 'glTF 2.0 format',
+ 'author': 'Julien Duroure, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
+ "version": (1, 3, 13),
+ 'blender': (2, 90, 0),
+ 'location': 'File > Import-Export',
+ 'description': 'Import-Export as glTF 2.0',
+ 'warning': '',
+ 'doc_url': "{BLENDER_MANUAL_URL}/addons/import_export/scene_gltf2.html",
+ 'tracker_url': "https://github.com/KhronosGroup/glTF-Blender-IO/issues/",
+ 'support': 'OFFICIAL',
+ 'category': 'Import-Export',
+}
+
+def get_version_string():
+ return str(bl_info['version'][0]) + '.' + str(bl_info['version'][1]) + '.' + str(bl_info['version'][2])
+
+#
+# Script reloading (if the user calls 'Reload Scripts' from Blender)
+#
+
+def reload_package(module_dict_main):
+ import importlib
+ from pathlib import Path
+
+ def reload_package_recursive(current_dir, module_dict):
+ for path in current_dir.iterdir():
+ if "__init__" in str(path) or path.stem not in module_dict:
+ continue
+
+ if path.is_file() and path.suffix == ".py":
+ importlib.reload(module_dict[path.stem])
+ elif path.is_dir():
+ reload_package_recursive(path, module_dict[path.stem].__dict__)
+
+ reload_package_recursive(Path(__file__).parent, module_dict_main)
+
+
+if "bpy" in locals():
+ reload_package(locals())
+
+import bpy
+from bpy.props import (StringProperty,
+ BoolProperty,
+ EnumProperty,
+ IntProperty,
+ CollectionProperty)
+from bpy.types import Operator
+from bpy_extras.io_utils import ImportHelper, ExportHelper
+
+
+#
+# Functions / Classes.
+#
+
+extension_panel_unregister_functors = []
+
+class ExportGLTF2_Base:
+ # TODO: refactor to avoid boilerplate
+
+ def __init__(self):
+ from io_scene_gltf2.io.exp import gltf2_io_draco_compression_extension
+ self.is_draco_available = gltf2_io_draco_compression_extension.dll_exists()
+
+ bl_options = {'PRESET'}
+
+ export_format: EnumProperty(
+ name='Format',
+ items=(('GLB', 'glTF Binary (.glb)',
+ 'Exports a single file, with all data packed in binary form. '
+ 'Most efficient and portable, but more difficult to edit later'),
+ ('GLTF_EMBEDDED', 'glTF Embedded (.gltf)',
+ 'Exports a single file, with all data packed in JSON. '
+ 'Less efficient than binary, but easier to edit later'),
+ ('GLTF_SEPARATE', 'glTF Separate (.gltf + .bin + textures)',
+ 'Exports multiple files, with separate JSON, binary and texture data. '
+ 'Easiest to edit later')),
+ description=(
+ 'Output format and embedding options. Binary is most efficient, '
+ 'but JSON (embedded or separate) may be easier to edit later'
+ ),
+ default='GLB'
+ )
+
+ ui_tab: EnumProperty(
+ items=(('GENERAL', "General", "General settings"),
+ ('MESHES', "Meshes", "Mesh settings"),
+ ('OBJECTS', "Objects", "Object settings"),
+ ('ANIMATION', "Animation", "Animation settings")),
+ name="ui_tab",
+ description="Export setting categories",
+ )
+
+ export_copyright: StringProperty(
+ name='Copyright',
+ description='Legal rights and conditions for the model',
+ default=''
+ )
+
+ export_image_format: EnumProperty(
+ name='Images',
+ items=(('AUTO', 'Automatic',
+ 'Save PNGs as PNGs and JPEGs as JPEGs.\n'
+ 'If neither one, use PNG'),
+ ('JPEG', 'JPEG Format (.jpg)',
+ 'Save images as JPEGs. (Images that need alpha are saved as PNGs though.)\n'
+ 'Be aware of a possible loss in quality'),
+ ),
+ description=(
+ 'Output format for images. PNG is lossless and generally preferred, but JPEG might be preferable for web '
+ 'applications due to the smaller file size'
+ ),
+ default='AUTO'
+ )
+
+ export_texture_dir: StringProperty(
+ name='Textures',
+ description='Folder to place texture files in. Relative to the .gltf file',
+ default='',
+ )
+
+ export_texcoords: BoolProperty(
+ name='UVs',
+ description='Export UVs (texture coordinates) with meshes',
+ default=True
+ )
+
+ export_normals: BoolProperty(
+ name='Normals',
+ description='Export vertex normals with meshes',
+ default=True
+ )
+
+ export_draco_mesh_compression_enable: BoolProperty(
+ name='Draco mesh compression',
+ description='Compress mesh using Draco',
+ default=False
+ )
+
+ export_draco_mesh_compression_level: IntProperty(
+ name='Compression level',
+ description='Compression level (0 = most speed, 6 = most compression, higher values currently not supported)',
+ default=6,
+ min=0,
+ max=6
+ )
+
+ export_draco_position_quantization: IntProperty(
+ name='Position quantization bits',
+ description='Quantization bits for position values (0 = no quantization)',
+ default=14,
+ min=0,
+ max=30
+ )
+
+ export_draco_normal_quantization: IntProperty(
+ name='Normal quantization bits',
+ description='Quantization bits for normal values (0 = no quantization)',
+ default=10,
+ min=0,
+ max=30
+ )
+
+ export_draco_texcoord_quantization: IntProperty(
+ name='Texcoord quantization bits',
+ description='Quantization bits for texture coordinate values (0 = no quantization)',
+ default=12,
+ min=0,
+ max=30
+ )
+
+ export_draco_generic_quantization: IntProperty(
+ name='Generic quantization bits',
+ description='Quantization bits for generic coordinate values like weights or joints (0 = no quantization)',
+ default=12,
+ min=0,
+ max=30
+ )
+
+ export_tangents: BoolProperty(
+ name='Tangents',
+ description='Export vertex tangents with meshes',
+ default=False
+ )
+
+ export_materials: BoolProperty(
+ name='Materials',
+ description='Export materials',
+ default=True
+ )
+
+ export_colors: BoolProperty(
+ name='Vertex Colors',
+ description='Export vertex colors with meshes',
+ default=True
+ )
+
+ export_cameras: BoolProperty(
+ name='Cameras',
+ description='Export cameras',
+ default=False
+ )
+
+ # keep it for compatibility (for now)
+ export_selected: BoolProperty(
+ name='Selected Objects',
+ description='Export selected objects only',
+ default=False
+ )
+
+ use_selection: BoolProperty(
+ name='Selected Objects',
+ description='Export selected objects only',
+ default=False
+ )
+
+ export_extras: BoolProperty(
+ name='Custom Properties',
+ description='Export custom properties as glTF extras',
+ default=False
+ )
+
+ export_yup: BoolProperty(
+ name='+Y Up',
+ description='Export using glTF convention, +Y up',
+ default=True
+ )
+
+ export_apply: BoolProperty(
+ name='Apply Modifiers',
+ description='Apply modifiers (excluding Armatures) to mesh objects -'
+ 'WARNING: prevents exporting shape keys',
+ default=False
+ )
+
+ export_animations: BoolProperty(
+ name='Animations',
+ description='Exports active actions and NLA tracks as glTF animations',
+ default=True
+ )
+
+ export_frame_range: BoolProperty(
+ name='Limit to Playback Range',
+ description='Clips animations to selected playback range',
+ default=True
+ )
+
+ export_frame_step: IntProperty(
+ name='Sampling Rate',
+ description='How often to evaluate animated values (in frames)',
+ default=1,
+ min=1,
+ max=120
+ )
+
+ export_force_sampling: BoolProperty(
+ name='Always Sample Animations',
+ description='Apply sampling to all animations',
+ default=True
+ )
+
+ export_nla_strips: BoolProperty(
+ name='Group by NLA Track',
+ description=(
+ "When on, multiple actions become part of the same glTF animation if\n"
+ "they're pushed onto NLA tracks with the same name.\n"
+ "When off, all the currently assigned actions become one glTF animation"
+ ),
+ default=True
+ )
+
+ export_def_bones: BoolProperty(
+ name='Export Deformation Bones Only',
+ description='Export Deformation bones only (and needed bones for hierarchy)',
+ default=False
+ )
+
+ export_current_frame: BoolProperty(
+ name='Use Current Frame',
+ description='Export the scene in the current animation frame',
+ default=False
+ )
+
+ export_skins: BoolProperty(
+ name='Skinning',
+ description='Export skinning (armature) data',
+ default=True
+ )
+
+ export_all_influences: BoolProperty(
+ name='Include All Bone Influences',
+ description='Allow >4 joint vertex influences. Models may appear incorrectly in many viewers',
+ default=False
+ )
+
+ export_morph: BoolProperty(
+ name='Shape Keys',
+ description='Export shape keys (morph targets)',
+ default=True
+ )
+
+ export_morph_normal: BoolProperty(
+ name='Shape Key Normals',
+ description='Export vertex normals with shape keys (morph targets)',
+ default=True
+ )
+
+ export_morph_tangent: BoolProperty(
+ name='Shape Key Tangents',
+ description='Export vertex tangents with shape keys (morph targets)',
+ default=False
+ )
+
+ export_lights: BoolProperty(
+ name='Punctual Lights',
+ description='Export directional, point, and spot lights. '
+ 'Uses "KHR_lights_punctual" glTF extension',
+ default=False
+ )
+
+ export_displacement: BoolProperty(
+ name='Displacement Textures (EXPERIMENTAL)',
+ description='EXPERIMENTAL: Export displacement textures. '
+ 'Uses incomplete "KHR_materials_displacement" glTF extension',
+ default=False
+ )
+
+ will_save_settings: BoolProperty(
+ name='Remember Export Settings',
+ description='Store glTF export settings in the Blender project',
+ default=False)
+
+ # Custom scene property for saving settings
+ scene_key = "glTF2ExportSettings"
+
+ #
+
+ def invoke(self, context, event):
+ settings = context.scene.get(self.scene_key)
+ self.will_save_settings = False
+ self.has_active_extenions = False
+ if settings:
+ try:
+ for (k, v) in settings.items():
+ if k == "export_selected": # Back compatibility for export_selected --> use_selection
+ setattr(self, "use_selection", v)
+ del settings[k]
+ settings["use_selection"] = v
+ print("export_selected is now renamed use_selection, and will be deleted in a few release")
+ else:
+ setattr(self, k, v)
+ self.will_save_settings = True
+
+ except (AttributeError, TypeError):
+ self.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings")
+ del context.scene[self.scene_key]
+
+ import sys
+ preferences = bpy.context.preferences
+ for addon_name in preferences.addons.keys():
+ try:
+ if hasattr(sys.modules[addon_name], 'glTF2ExportUserExtension') or hasattr(sys.modules[addon_name], 'glTF2ExportUserExtensions'):
+ extension_panel_unregister_functors.append(sys.modules[addon_name].register_panel())
+ self.has_active_extenions = True
+ except Exception:
+ pass
+
+ return ExportHelper.invoke(self, context, event)
+
+ def save_settings(self, context):
+ # find all export_ props
+ all_props = self.properties
+ export_props = {x: getattr(self, x) for x in dir(all_props)
+ if x.startswith("export_") and all_props.get(x) is not None}
+
+ context.scene[self.scene_key] = export_props
+
+ def execute(self, context):
+ import os
+ import datetime
+ from .blender.exp import gltf2_blender_export
+
+ if self.will_save_settings:
+ self.save_settings(context)
+
+ if self.export_format == 'GLB':
+ self.filename_ext = '.glb'
+ else:
+ self.filename_ext = '.gltf'
+
+ # All custom export settings are stored in this container.
+ export_settings = {}
+
+ export_settings['timestamp'] = datetime.datetime.now()
+
+ export_settings['gltf_filepath'] = bpy.path.ensure_ext(self.filepath, self.filename_ext)
+ export_settings['gltf_filedirectory'] = os.path.dirname(export_settings['gltf_filepath']) + '/'
+ export_settings['gltf_texturedirectory'] = os.path.join(
+ export_settings['gltf_filedirectory'],
+ self.export_texture_dir,
+ )
+
+ export_settings['gltf_format'] = self.export_format
+ export_settings['gltf_image_format'] = self.export_image_format
+ export_settings['gltf_copyright'] = self.export_copyright
+ export_settings['gltf_texcoords'] = self.export_texcoords
+ export_settings['gltf_normals'] = self.export_normals
+ export_settings['gltf_tangents'] = self.export_tangents and self.export_normals
+
+ if self.is_draco_available:
+ export_settings['gltf_draco_mesh_compression'] = self.export_draco_mesh_compression_enable
+ export_settings['gltf_draco_mesh_compression_level'] = self.export_draco_mesh_compression_level
+ export_settings['gltf_draco_position_quantization'] = self.export_draco_position_quantization
+ export_settings['gltf_draco_normal_quantization'] = self.export_draco_normal_quantization
+ export_settings['gltf_draco_texcoord_quantization'] = self.export_draco_texcoord_quantization
+ export_settings['gltf_draco_generic_quantization'] = self.export_draco_generic_quantization
+ else:
+ export_settings['gltf_draco_mesh_compression'] = False
+
+ export_settings['gltf_materials'] = self.export_materials
+ export_settings['gltf_colors'] = self.export_colors
+ export_settings['gltf_cameras'] = self.export_cameras
+
+ # compatibility after renaming export_selected to use_selection
+ if self.export_selected is True:
+ self.report({"WARNING"}, "export_selected is now renamed use_selection, and will be deleted in a few release")
+ export_settings['gltf_selected'] = self.export_selected
+ else:
+ export_settings['gltf_selected'] = self.use_selection
+
+ # export_settings['gltf_selected'] = self.use_selection This can be uncomment when removing compatibility of export_selected
+ export_settings['gltf_layers'] = True # self.export_layers
+ export_settings['gltf_extras'] = self.export_extras
+ export_settings['gltf_yup'] = self.export_yup
+ export_settings['gltf_apply'] = self.export_apply
+ export_settings['gltf_current_frame'] = self.export_current_frame
+ export_settings['gltf_animations'] = self.export_animations
+ if self.export_animations:
+ export_settings['gltf_frame_range'] = self.export_frame_range
+ export_settings['gltf_force_sampling'] = self.export_force_sampling
+ if self.export_force_sampling:
+ export_settings['gltf_def_bones'] = self.export_def_bones
+ else:
+ export_settings['gltf_def_bones'] = False
+ export_settings['gltf_nla_strips'] = self.export_nla_strips
+ else:
+ export_settings['gltf_frame_range'] = False
+ export_settings['gltf_move_keyframes'] = False
+ export_settings['gltf_force_sampling'] = False
+ export_settings['gltf_def_bones'] = False
+ export_settings['gltf_skins'] = self.export_skins
+ if self.export_skins:
+ export_settings['gltf_all_vertex_influences'] = self.export_all_influences
+ else:
+ export_settings['gltf_all_vertex_influences'] = False
+ export_settings['gltf_frame_step'] = self.export_frame_step
+ export_settings['gltf_morph'] = self.export_morph
+ if self.export_morph:
+ export_settings['gltf_morph_normal'] = self.export_morph_normal
+ else:
+ export_settings['gltf_morph_normal'] = False
+ if self.export_morph and self.export_morph_normal:
+ export_settings['gltf_morph_tangent'] = self.export_morph_tangent
+ else:
+ export_settings['gltf_morph_tangent'] = False
+
+ export_settings['gltf_lights'] = self.export_lights
+ export_settings['gltf_displacement'] = self.export_displacement
+
+ export_settings['gltf_binary'] = bytearray()
+ export_settings['gltf_binaryfilename'] = os.path.splitext(os.path.basename(
+ bpy.path.ensure_ext(self.filepath,self.filename_ext)))[0] + '.bin'
+
+ user_extensions = []
+
+ import sys
+ preferences = bpy.context.preferences
+ for addon_name in preferences.addons.keys():
+ try:
+ module = sys.modules[addon_name]
+ except Exception:
+ continue
+ if hasattr(module, 'glTF2ExportUserExtension'):
+ extension_ctor = module.glTF2ExportUserExtension
+ user_extensions.append(extension_ctor())
+ if hasattr(module, 'glTF2ExportUserExtensions'):
+ extension_ctors = module.glTF2ExportUserExtensions
+ for extension_ctor in extension_ctors:
+ user_extensions.append(extension_ctor())
+ export_settings['gltf_user_extensions'] = user_extensions
+
+ return gltf2_blender_export.save(context, export_settings)
+
+ def draw(self, context):
+ pass # Is needed to get panels available
+
+
+class GLTF_PT_export_main(bpy.types.Panel):
+ bl_space_type = 'FILE_BROWSER'
+ bl_region_type = 'TOOL_PROPS'
+ bl_label = ""
+ bl_parent_id = "FILE_PT_operator"
+ bl_options = {'HIDE_HEADER'}
+
+ @classmethod
+ def poll(cls, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ layout.prop(operator, 'export_format')
+ if operator.export_format == 'GLTF_SEPARATE':
+ layout.prop(operator, 'export_texture_dir', icon='FILE_FOLDER')
+ layout.prop(operator, 'export_copyright')
+ layout.prop(operator, 'will_save_settings')
+
+
+class GLTF_PT_export_include(bpy.types.Panel):
+ bl_space_type = 'FILE_BROWSER'
+ bl_region_type = 'TOOL_PROPS'
+ bl_label = "Include"
+ bl_parent_id = "FILE_PT_operator"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ col = layout.column(heading = "Limit to", align = True)
+ col.prop(operator, 'use_selection')
+
+ col = layout.column(heading = "Data", align = True)
+ col.prop(operator, 'export_extras')
+ col.prop(operator, 'export_cameras')
+ col.prop(operator, 'export_lights')
+
+
+class GLTF_PT_export_transform(bpy.types.Panel):
+ bl_space_type = 'FILE_BROWSER'
+ bl_region_type = 'TOOL_PROPS'
+ bl_label = "Transform"
+ bl_parent_id = "FILE_PT_operator"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ layout.prop(operator, 'export_yup')
+
+
+class GLTF_PT_export_geometry(bpy.types.Panel):
+ bl_space_type = 'FILE_BROWSER'
+ bl_region_type = 'TOOL_PROPS'
+ bl_label = "Geometry"
+ bl_parent_id = "FILE_PT_operator"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ layout.prop(operator, 'export_apply')
+ layout.prop(operator, 'export_texcoords')
+ layout.prop(operator, 'export_normals')
+ col = layout.column()
+ col.active = operator.export_normals
+ col.prop(operator, 'export_tangents')
+ layout.prop(operator, 'export_colors')
+ layout.prop(operator, 'export_materials')
+ col = layout.column()
+ col.active = operator.export_materials
+ col.prop(operator, 'export_image_format')
+
+
+class GLTF_PT_export_geometry_compression(bpy.types.Panel):
+ bl_space_type = 'FILE_BROWSER'
+ bl_region_type = 'TOOL_PROPS'
+ bl_label = "Compression"
+ bl_parent_id = "GLTF_PT_export_geometry"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def __init__(self):
+ from io_scene_gltf2.io.exp import gltf2_io_draco_compression_extension
+ self.is_draco_available = gltf2_io_draco_compression_extension.dll_exists(quiet=True)
+
+ @classmethod
+ def poll(cls, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+ if operator.is_draco_available:
+ return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
+
+ def draw_header(self, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+ self.layout.prop(operator, "export_draco_mesh_compression_enable", text="")
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ layout.active = operator.export_draco_mesh_compression_enable
+ layout.prop(operator, 'export_draco_mesh_compression_level')
+
+ col = layout.column(align=True)
+ col.prop(operator, 'export_draco_position_quantization', text="Quantize Position")
+ col.prop(operator, 'export_draco_normal_quantization', text="Normal")
+ col.prop(operator, 'export_draco_texcoord_quantization', text="Tex Coords")
+ col.prop(operator, 'export_draco_generic_quantization', text="Generic")
+
+
+class GLTF_PT_export_animation(bpy.types.Panel):
+ bl_space_type = 'FILE_BROWSER'
+ bl_region_type = 'TOOL_PROPS'
+ bl_label = "Animation"
+ bl_parent_id = "FILE_PT_operator"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ layout.prop(operator, 'export_current_frame')
+
+
+class GLTF_PT_export_animation_export(bpy.types.Panel):
+ bl_space_type = 'FILE_BROWSER'
+ bl_region_type = 'TOOL_PROPS'
+ bl_label = "Animation"
+ bl_parent_id = "GLTF_PT_export_animation"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
+
+ def draw_header(self, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+ self.layout.prop(operator, "export_animations", text="")
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ layout.active = operator.export_animations
+
+ layout.prop(operator, 'export_frame_range')
+ layout.prop(operator, 'export_frame_step')
+ layout.prop(operator, 'export_force_sampling')
+ layout.prop(operator, 'export_nla_strips')
+
+ row = layout.row()
+ row.active = operator.export_force_sampling
+ row.prop(operator, 'export_def_bones')
+
+
+class GLTF_PT_export_animation_shapekeys(bpy.types.Panel):
+ bl_space_type = 'FILE_BROWSER'
+ bl_region_type = 'TOOL_PROPS'
+ bl_label = "Shape Keys"
+ bl_parent_id = "GLTF_PT_export_animation"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
+
+ def draw_header(self, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+ self.layout.prop(operator, "export_morph", text="")
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ layout.active = operator.export_morph
+
+ layout.prop(operator, 'export_morph_normal')
+ col = layout.column()
+ col.active = operator.export_morph_normal
+ col.prop(operator, 'export_morph_tangent')
+
+
+class GLTF_PT_export_animation_skinning(bpy.types.Panel):
+ bl_space_type = 'FILE_BROWSER'
+ bl_region_type = 'TOOL_PROPS'
+ bl_label = "Skinning"
+ bl_parent_id = "GLTF_PT_export_animation"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
+
+ def draw_header(self, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+ self.layout.prop(operator, "export_skins", text="")
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ layout.active = operator.export_skins
+ layout.prop(operator, 'export_all_influences')
+
+class GLTF_PT_export_user_extensions(bpy.types.Panel):
+ bl_space_type = 'FILE_BROWSER'
+ bl_region_type = 'TOOL_PROPS'
+ bl_label = "Extensions"
+ bl_parent_id = "FILE_PT_operator"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ return operator.bl_idname == "EXPORT_SCENE_OT_gltf" and operator.has_active_extenions
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+
+class ExportGLTF2(bpy.types.Operator, ExportGLTF2_Base, ExportHelper):
+ """Export scene as glTF 2.0 file"""
+ bl_idname = 'export_scene.gltf'
+ bl_label = 'Export glTF 2.0'
+
+ filename_ext = ''
+
+ filter_glob: StringProperty(default='*.glb;*.gltf', options={'HIDDEN'})
+
+
+def menu_func_export(self, context):
+ self.layout.operator(ExportGLTF2.bl_idname, text='glTF 2.0 (.glb/.gltf)')
+
+
+class ImportGLTF2(Operator, ImportHelper):
+ """Load a glTF 2.0 file"""
+ bl_idname = 'import_scene.gltf'
+ bl_label = 'Import glTF 2.0'
+
+ filter_glob: StringProperty(default="*.glb;*.gltf", options={'HIDDEN'})
+
+ files: CollectionProperty(
+ name="File Path",
+ type=bpy.types.OperatorFileListElement,
+ )
+
+ loglevel: IntProperty(
+ name='Log Level',
+ description="Log Level")
+
+ import_pack_images: BoolProperty(
+ name='Pack Images',
+ description='Pack all images into .blend file',
+ default=True
+ )
+
+ import_shading: EnumProperty(
+ name="Shading",
+ items=(("NORMALS", "Use Normal Data", ""),
+ ("FLAT", "Flat Shading", ""),
+ ("SMOOTH", "Smooth Shading", "")),
+ description="How normals are computed during import",
+ default="NORMALS")
+
+ bone_heuristic: EnumProperty(
+ name="Bone Dir",
+ items=(
+ ("BLENDER", "Blender (best for re-importing)",
+ "Good for re-importing glTFs exported from Blender.\n"
+ "Bone tips are placed on their local +Y axis (in glTF space)"),
+ ("TEMPERANCE", "Temperance (average)",
+ "Decent all-around strategy.\n"
+ "A bone with one child has its tip placed on the local axis\n"
+ "closest to its child"),
+ ("FORTUNE", "Fortune (may look better, less accurate)",
+ "Might look better than Temperance, but also might have errors.\n"
+ "A bone with one child has its tip placed at its child's root.\n"
+ "Non-uniform scalings may get messed up though, so beware"),
+ ),
+ description="Heuristic for placing bones. Tries to make bones pretty",
+ default="TEMPERANCE",
+ )
+
+ guess_original_bind_pose: BoolProperty(
+ name='Guess Original Bind Pose',
+ description=(
+ 'Try to guess the original bind pose for skinned meshes from '
+ 'the inverse bind matrices.\n'
+ 'When off, use default/rest pose as bind pose'
+ ),
+ default=True,
+ )
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ layout.prop(self, 'import_pack_images')
+ layout.prop(self, 'import_shading')
+ layout.prop(self, 'guess_original_bind_pose')
+ layout.prop(self, 'bone_heuristic')
+
+ def execute(self, context):
+ return self.import_gltf2(context)
+
+ def import_gltf2(self, context):
+ import os
+
+ self.set_debug_log()
+ import_settings = self.as_keywords()
+
+ if self.files:
+ # Multiple file import
+ ret = {'CANCELLED'}
+ dirname = os.path.dirname(self.filepath)
+ for file in self.files:
+ path = os.path.join(dirname, file.name)
+ if self.unit_import(path, import_settings) == {'FINISHED'}:
+ ret = {'FINISHED'}
+ return ret
+ else:
+ # Single file import
+ return self.unit_import(self.filepath, import_settings)
+
+ def unit_import(self, filename, import_settings):
+ import time
+ from .io.imp.gltf2_io_gltf import glTFImporter
+ from .blender.imp.gltf2_blender_gltf import BlenderGlTF
+
+ self.gltf_importer = glTFImporter(filename, import_settings)
+ success, txt = self.gltf_importer.read()
+ if not success:
+ self.report({'ERROR'}, txt)
+ return {'CANCELLED'}
+ success, txt = self.gltf_importer.checks()
+ if not success:
+ self.report({'ERROR'}, txt)
+ return {'CANCELLED'}
+ print("Data are loaded, start creating Blender stuff")
+ start_time = time.time()
+ BlenderGlTF.create(self.gltf_importer)
+ elapsed_s = "{:.2f}s".format(time.time() - start_time)
+ print("glTF import finished in " + elapsed_s)
+ self.gltf_importer.log.removeHandler(self.gltf_importer.log_handler)
+
+ return {'FINISHED'}
+
+ def set_debug_log(self):
+ import logging
+ if bpy.app.debug_value == 0:
+ self.loglevel = logging.CRITICAL
+ elif bpy.app.debug_value == 1:
+ self.loglevel = logging.ERROR
+ elif bpy.app.debug_value == 2:
+ self.loglevel = logging.WARNING
+ elif bpy.app.debug_value == 3:
+ self.loglevel = logging.INFO
+ else:
+ self.loglevel = logging.NOTSET
+
+
+def menu_func_import(self, context):
+ self.layout.operator(ImportGLTF2.bl_idname, text='glTF 2.0 (.glb/.gltf)')
+
+
+classes = (
+ ExportGLTF2,
+ GLTF_PT_export_main,
+ GLTF_PT_export_include,
+ GLTF_PT_export_transform,
+ GLTF_PT_export_geometry,
+ GLTF_PT_export_geometry_compression,
+ GLTF_PT_export_animation,
+ GLTF_PT_export_animation_export,
+ GLTF_PT_export_animation_shapekeys,
+ GLTF_PT_export_animation_skinning,
+ GLTF_PT_export_user_extensions,
+ ImportGLTF2
+)
+
+
+def register():
+ for c in classes:
+ bpy.utils.register_class(c)
+ # bpy.utils.register_module(__name__)
+
+ # add to the export / import menu
+ bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
+ bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
+
+
+def unregister():
+ for c in classes:
+ bpy.utils.unregister_class(c)
+ for f in extension_panel_unregister_functors:
+ f()
+ extension_panel_unregister_functors.clear()
+
+ # bpy.utils.unregister_module(__name__)
+
+ # remove from the export / import menu
+ bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
+ bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)