diff options
Diffstat (limited to 'io_scene_gltf2/__init__.py')
-rwxr-xr-x | io_scene_gltf2/__init__.py | 520 |
1 files changed, 520 insertions, 0 deletions
diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py new file mode 100755 index 00000000..0318919a --- /dev/null +++ b/io_scene_gltf2/__init__.py @@ -0,0 +1,520 @@ +# Copyright 2018 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. + +# +# Imports +# + +import os +import bpy +from bpy_extras.io_utils import ImportHelper, ExportHelper +from bpy.types import Operator, AddonPreferences + +from .io.com.gltf2_io_debug import Log + +from bpy.props import (CollectionProperty, + StringProperty, + BoolProperty, + EnumProperty, + FloatProperty, + IntProperty) + +# +# Globals +# + +bl_info = { + 'name': 'glTF 2.0 format', + 'author': 'Julien Duroure, Norbert Nopper, Urs Hanselmann Moritz Becher, Benjamin Schmithüsen', + "version": (0, 0, 1), + 'blender': (2, 80, 0), + 'location': 'File > Import-Export', + 'description': 'Import-Export as glTF 2.0', + 'warning': '', + 'wiki_url': "https://github.com/KhronosGroup/glTF-Blender-IO", + 'tracker_url': "https://github.com/KhronosGroup/glTF-Blender-IO/issues/", + 'support': 'OFFICIAL', + 'category': 'Import-Export'} + + +# +# Functions / Classes. +# + + +class ExportGLTF2_Base: + + # TODO: refactor to avoid boilerplate + + bl_options = {'UNDO', '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"), + ('MATERIALS', "Materials", "Material 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_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_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 + ) + + export_selected: BoolProperty( + name='Selected Objects', + description='Export selected objects only', + default=False + ) + + # export_layers: BoolProperty( + # name='All layers', + # description='Export all layers, rather than just the first', + # default=True + # ) + + 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 to mesh objects', + 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_move_keyframes: BoolProperty( + name='Keyframes Start at 0', + description='Keyframes start at 0, instead of 1', + default=True + ) + + export_force_sampling: BoolProperty( + name='Always Sample Animations', + description='Apply sampling to all animations', + default=False + ) + + export_current_frame: BoolProperty( + name='Use Current Frame', + description='Export the scene in the current animation frame', + default=True + ) + + export_skins: BoolProperty( + name='Skinning', + description='Export skinning (armature) data', + default=True + ) + + export_bake_skins: BoolProperty( + name='Bake Skinning Constraints', + description='Apply skinning constraints to armatures', + default=False + ) + + 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_texture_transform: BoolProperty( + name='Texture Transforms', + description='Export texture or UV position, rotation, and scale. ' + 'Uses "KHR_texture_transform" 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(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 + if settings: + try: + for (k, v) in settings.items(): + 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] + + 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 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_format'] = self.export_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 + export_settings['gltf_materials'] = self.export_materials + export_settings['gltf_colors'] = self.export_colors + export_settings['gltf_cameras'] = self.export_cameras + export_settings['gltf_selected'] = self.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_animations'] = self.export_animations + if self.export_animations: + export_settings['gltf_current_frame'] = False + export_settings['gltf_frame_range'] = self.export_frame_range + export_settings['gltf_move_keyframes'] = self.export_move_keyframes + export_settings['gltf_force_sampling'] = self.export_force_sampling + else: + export_settings['gltf_current_frame'] = self.export_current_frame + export_settings['gltf_frame_range'] = False + export_settings['gltf_move_keyframes'] = False + export_settings['gltf_force_sampling'] = False + export_settings['gltf_skins'] = self.export_skins + if self.export_skins: + export_settings['gltf_bake_skins'] = self.export_bake_skins + export_settings['gltf_all_vertex_influences'] = self.export_all_influences + else: + export_settings['gltf_bake_skins'] = False + 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_texture_transform'] = self.export_texture_transform + export_settings['gltf_displacement'] = self.export_displacement + + export_settings['gltf_binary'] = bytearray() + export_settings['gltf_binaryfilename'] = os.path.splitext(os.path.basename(self.filepath))[0] + '.bin' + + return gltf2_blender_export.save(context, export_settings) + + def draw(self, context): + self.layout.prop(self, 'ui_tab', expand=True) + if self.ui_tab == 'GENERAL': + self.draw_general_settings() + elif self.ui_tab == 'MESHES': + self.draw_mesh_settings() + elif self.ui_tab == 'OBJECTS': + self.draw_object_settings() + elif self.ui_tab == 'MATERIALS': + self.draw_material_settings() + elif self.ui_tab == 'ANIMATION': + self.draw_animation_settings() + + def draw_general_settings(self): + col = self.layout.box().column() + col.prop(self, 'export_format') + col.prop(self, 'export_selected') + col.prop(self, 'export_apply') + col.prop(self, 'export_yup') + col.prop(self, 'export_extras') + col.prop(self, 'export_copyright') + + def draw_mesh_settings(self): + col = self.layout.box().column() + col.prop(self, 'export_texcoords') + col.prop(self, 'export_normals') + if self.export_normals: + col.prop(self, 'export_tangents') + col.prop(self, 'export_colors') + + def draw_object_settings(self): + col = self.layout.box().column() + col.prop(self, 'export_cameras') + col.prop(self, 'export_lights') + + def draw_material_settings(self): + col = self.layout.box().column() + col.prop(self, 'export_materials') + col.prop(self, 'export_texture_transform') + + def draw_animation_settings(self): + col = self.layout.box().column() + col.prop(self, 'export_animations') + if self.export_animations: + col.prop(self, 'export_frame_range') + col.prop(self, 'export_frame_step') + col.prop(self, 'export_move_keyframes') + col.prop(self, 'export_force_sampling') + else: + col.prop(self, 'export_current_frame') + col.prop(self, 'export_skins') + if self.export_skins: + col.prop(self, 'export_bake_skins') + col.prop(self, 'export_all_influences') + col.prop(self, 'export_morph') + if self.export_morph: + col.prop(self, 'export_morph_normal') + if self.export_morph_normal: + col.prop(self, 'export_morph_tangent') + + +class ExportGLTF2(bpy.types.Operator, ExportGLTF2_Base, ExportHelper): + """Export scene as glTF 2.0 file""" + bl_idname = 'export_scene.gltf' + bl_label = 'glTF 2.0 (.glb/.gltf)' + + 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): + bl_idname = 'import_scene.gltf' + bl_label = 'glTF 2.0 (.glb/.gltf)' + + filter_glob: StringProperty(default="*.glb;*.gltf", options={'HIDDEN'}) + + loglevel: EnumProperty( + items=Log.get_levels(), + name="Log Level", + description="Set level of log to display", + default=Log.default()) + + 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") + + def draw(self, context): + layout = self.layout + + layout.prop(self, 'loglevel') + layout.prop(self, 'import_pack_images') + layout.prop(self, 'import_shading') + + def execute(self, context): + return self.import_gltf2(context) + + def import_gltf2(self, context): + from .io.imp.gltf2_io_gltf import glTFImporter + from .blender.imp.gltf2_blender_gltf import BlenderGlTF + + import_settings = self.as_keywords() + + self.gltf_importer = glTFImporter(self.filepath, 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'} + self.gltf_importer.log.critical("Data are loaded, start creating Blender stuff") + BlenderGlTF.create(self.gltf_importer) + self.gltf_importer.log.critical("glTF import is now finished") + self.gltf_importer.log.removeHandler(self.gltf_importer.log_handler) + + return {'FINISHED'} + + +def menu_func_import(self, context): + self.layout.operator(ImportGLTF2.bl_idname, text=ImportGLTF2.bl_label) + + +classes = ( + ExportGLTF2, + 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) + # 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) + |