Skip to content
Snippets Groups Projects
__init__.py 46.2 KiB
Newer Older
  • Learn to ignore specific revisions
  •         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')
    
    
            row = layout.row()
            row.active = operator.export_force_sampling
            row.prop(operator, 'export_def_bones')
            if operator.export_force_sampling is False and operator.export_def_bones is True:
                layout.label(text="Export only deformation bones is not possible when not sampling animation")
    
    
    class GLTF_PT_export_user_extensions(bpy.types.Panel):
        bl_space_type = 'FILE_BROWSER'
        bl_region_type = 'TOOL_PROPS'
    
        bl_label = "Exporter 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_exporter_extensions
    
    
        def draw(self, context):
            layout = self.layout
            layout.use_property_split = True
            layout.use_property_decorate = False  # No animation.
    
    
    class GLTF_PT_import_user_extensions(bpy.types.Panel):
        bl_space_type = 'FILE_BROWSER'
        bl_region_type = 'TOOL_PROPS'
        bl_label = "Importer 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 == "IMPORT_SCENE_OT_gltf" and operator.has_active_importer_extensions
    
        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'
    
        bl_options = {'REGISTER', 'UNDO'}
    
    
        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
        )
    
    
        merge_vertices: BoolProperty(
            name='Merge Vertices',
            description=(
                'The glTF format requires discontinuous normals, UVs, and '
                'other vertex attributes to be stored as separate vertices, '
    
    Julien Duroure's avatar
    Julien Duroure committed
                'as required for rendering on typical graphics hardware. '
                'This option attempts to combine co-located vertices where possible. '
    
                'Currently cannot combine verts with different normals'
            ),
            default=False,
        )
    
    
        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)",
    
    Julien Duroure's avatar
    Julien Duroure committed
                    "Good for re-importing glTFs exported from Blender. "
    
                    "Bone tips are placed on their local +Y axis (in glTF space)"),
                ("TEMPERANCE", "Temperance (average)",
    
    Julien Duroure's avatar
    Julien Duroure committed
                    "Decent all-around strategy. "
                    "A bone with one child has its tip placed on the local axis "
    
                    "closest to its child"),
                ("FORTUNE", "Fortune (may look better, less accurate)",
    
    Julien Duroure's avatar
    Julien Duroure committed
                    "Might look better than Temperance, but also might have errors. "
                    "A bone with one child has its tip placed at its child's root. "
    
                    "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 '
    
    Julien Duroure's avatar
    Julien Duroure committed
                'the inverse bind matrices. '
    
                '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, 'merge_vertices')
    
            layout.prop(self, 'import_shading')
            layout.prop(self, 'guess_original_bind_pose')
            layout.prop(self, 'bone_heuristic')
    
    
        def invoke(self, context, event):
            import sys
            preferences = bpy.context.preferences
            for addon_name in preferences.addons.keys():
                try:
                    if hasattr(sys.modules[addon_name], 'glTF2ImportUserExtension') or hasattr(sys.modules[addon_name], 'glTF2ImportUserExtensions'):
                        importer_extension_panel_unregister_functors.append(sys.modules[addon_name].register_panel())
                except Exception:
                    pass
    
            self.has_active_importer_extensions = len(importer_extension_panel_unregister_functors) > 0
            return ImportHelper.invoke(self, context, event)
    
    
        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()
    
    
            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, 'glTF2ImportUserExtension'):
                    extension_ctor = module.glTF2ImportUserExtension
                    user_extensions.append(extension_ctor())
            import_settings['import_user_extensions'] = user_extensions
    
    
            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, ImportError
    
            from .blender.imp.gltf2_blender_gltf import BlenderGlTF
    
    
            try:
                gltf_importer = glTFImporter(filename, import_settings)
                gltf_importer.read()
                gltf_importer.checks()
    
                print("Data are loaded, start creating Blender stuff")
    
                start_time = time.time()
                BlenderGlTF.create(gltf_importer)
                elapsed_s = "{:.2f}s".format(time.time() - start_time)
                print("glTF import finished in " + elapsed_s)
    
                gltf_importer.log.removeHandler(gltf_importer.log_handler)
    
                return {'FINISHED'}
    
            except ImportError as e:
                self.report({'ERROR'}, e.args[0])
    
                return {'CANCELLED'}
    
        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 gltf_variant_ui_update(self, context):
        from .blender.com.gltf2_blender_ui import variant_register, variant_unregister
        if self.KHR_materials_variants_ui is True:
            # register all needed types
            variant_register()
        else:
            variant_unregister()
    
    
    class GLTF_AddonPreferences(bpy.types.AddonPreferences):
        bl_idname = __package__
    
        settings_node_ui : bpy.props.BoolProperty(
                default= False,
    
                description="Displays glTF Material Output node in Shader Editor (Menu Add > Output)"
    
        KHR_materials_variants_ui : bpy.props.BoolProperty(
            default= False,
            description="Displays glTF UI to manage material variants",
            update=gltf_variant_ui_update
            )
    
    
    
        def draw(self, context):
            layout = self.layout
            row = layout.row()
            row.prop(self, "settings_node_ui", text="Shader Editor Add-ons")
    
            row.prop(self, "KHR_materials_variants_ui", text="Material Variants")
    
    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_mesh,
        GLTF_PT_export_geometry_material,
        GLTF_PT_export_geometry_original_pbr,
    
        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,
    
        GLTF_PT_import_user_extensions,
        GLTF_AddonPreferences
    
        import io_scene_gltf2.blender.com.gltf2_blender_ui as blender_ui
    
        for c in classes:
            bpy.utils.register_class(c)
        # bpy.utils.register_module(__name__)
    
    
        if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True:
            blender_ui.variant_register()
    
        # 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():
    
        import io_scene_gltf2.blender.com.gltf2_blender_ui as blender_ui
    
        blender_ui.unregister()
        if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True:
            blender_ui.variant_unregister()
    
    
        for c in classes:
            bpy.utils.unregister_class(c)
    
        for f in exporter_extension_panel_unregister_functors:
            f()
        exporter_extension_panel_unregister_functors.clear()
    
        for f in importer_extension_panel_unregister_functors:
    
        importer_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)