From 02243bda80624499965f0c96f8a32afe8aa2a754 Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Mon, 14 Mar 2022 08:53:52 -0400 Subject: [PATCH 01/31] fix: force absolute path on multi exporter (#58) --- addons/io_scene_gltf2_msfs/io/msfs_multi_export.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py b/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py index 7762e60..51267d5 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py @@ -116,9 +116,11 @@ def execute(self, context): xml_string = dom.toprettyxml(encoding="utf-8") with open( - os.path.join( - lod_group.folder_name, - lod_group.group_name + ".xml", + bpy.path.abspath( + os.path.join( + lod_group.folder_name, + lod_group.group_name + ".xml", + ) ), "wb", ) as f: @@ -149,7 +151,7 @@ def select_recursive(obj): MSFS_OT_MultiExportGLTF2.export( os.path.join( - lod_group.folder_name, + bpy.path.abspath(lod_group.folder_name), os.path.splitext(lod.file_name)[0], ) ) @@ -169,7 +171,7 @@ def select_recursive(obj): if obj in list(bpy.context.window.view_layer.objects): obj.select_set(True) - MSFS_OT_MultiExportGLTF2.export(os.path.join(preset.file_path)) + MSFS_OT_MultiExportGLTF2.export(bpy.path.abspath(preset.file_path)) return {"FINISHED"} From ccfecf51fe499bfeb4c31527a929a07f86d48f8f Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Mon, 14 Mar 2022 08:54:35 -0400 Subject: [PATCH 02/31] fix: remove alpha warning from readme (#60) --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 5d0cecd..d649ab9 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ FlightSim Blender glTF 2.0 Importer and Exporter ====================================== -#### For certain features to work, you need to have Blender version 3.1 Alpha or newer. Download here: https://builder.blender.org/download/daily/ This repository represents an alpha version of the official FlightSim-Blender Import/Export plugin. The flight sim community has already developed and forked the original project many times, and Asobo's intention is to fully support Blender with the contributions of all the developers that already developed many features in different unofficial Blender plugins. From 260632b0dd96253e61a83da7e31eb19e927535db Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Mon, 14 Mar 2022 08:56:44 -0400 Subject: [PATCH 03/31] fix: replace %xx escapes with character (#62) --- addons/io_scene_gltf2_msfs/io/msfs_export.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/io_scene_gltf2_msfs/io/msfs_export.py b/addons/io_scene_gltf2_msfs/io/msfs_export.py index fba48a8..f922c18 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_export.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_export.py @@ -16,6 +16,8 @@ import os import bpy +import urllib + from .. import get_version_string from .msfs_light import MSFSLight @@ -42,7 +44,7 @@ def gather_asset_hook(self, gltf2_asset, export_settings): def gather_gltf_extensions_hook(self, gltf2_plan, export_settings): if self.properties.enabled: for i, image in enumerate(gltf2_plan.images): - image.uri = os.path.basename(image.uri) + image.uri = os.path.basename(urllib.parse.unquote(image.uri)) def gather_node_hook(self, gltf2_object, blender_object, export_settings): if self.properties.enabled: From b530d110244e7b95a50e87a85964590d463f9419 Mon Sep 17 00:00:00 2001 From: lpierabella <77288191+lpierabella@users.noreply.github.com> Date: Mon, 14 Mar 2022 14:02:26 +0100 Subject: [PATCH 04/31] bump version --- addons/io_scene_gltf2_msfs/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/io_scene_gltf2_msfs/__init__.py b/addons/io_scene_gltf2_msfs/__init__.py index 0e4ded4..90a83b0 100644 --- a/addons/io_scene_gltf2_msfs/__init__.py +++ b/addons/io_scene_gltf2_msfs/__init__.py @@ -25,7 +25,7 @@ "author": "Luca Pierabella, Wing42, pepperoni505, ronh991, tml1024, and others", "description": "This toolkit prepares your 3D assets to be used for Microsoft Flight Simulator", "blender": (3, 1, 0), - "version": (1, 0, 4), + "version": (1, 1, 1), "location": "File > Import-Export", "category": "Import-Export", "tracker_url": "https://github.com/AsoboStudio/glTF-Blender-IO-MSFS" From b2f9e5cccab2d155eff6170ed7ffc8862fe1d9ec Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Tue, 15 Mar 2022 03:50:11 -0400 Subject: [PATCH 05/31] fix: properly migrate old material data (#63) --- .../blender/msfs_material_function.py | 44 +++++++++++++++++++ .../blender/msfs_material_panel.py | 10 ++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py index 6394ddb..9eb5685 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py @@ -17,6 +17,7 @@ import bpy from enum import Enum + class MSFS_MaterialProperties(Enum): baseColor = 0, "Base Color" emissive = 1, "Emissive" @@ -122,6 +123,7 @@ def __init__(self, material, buildTree=False, defaultPBR=False): self.links = material.node_tree.links if buildTree: self.__buildShaderTree() + self.force_update_properties() def revertToPBRShaderTree(self): self.cleanNodeTree() @@ -131,6 +133,48 @@ def __buildShaderTree(self): self.cleanNodeTree() self.createNodetree() + def force_update_properties(self): + from .msfs_material_prop_update import MSFS_Material_Property_Update + + MSFS_Material_Property_Update.update_base_color_texture( + self.material, bpy.context + ) + MSFS_Material_Property_Update.update_comp_texture(self.material, bpy.context) + MSFS_Material_Property_Update.update_normal_texture(self.material, bpy.context) + MSFS_Material_Property_Update.update_emissive_texture( + self.material, bpy.context + ) + MSFS_Material_Property_Update.update_detail_color_texture( + self.material, bpy.context + ) + MSFS_Material_Property_Update.update_detail_comp_texture( + self.material, bpy.context + ) + MSFS_Material_Property_Update.update_detail_normal_texture( + self.material, bpy.context + ) + MSFS_Material_Property_Update.update_blend_mask_texture( + self.material, bpy.context + ) + MSFS_Material_Property_Update.update_wetness_ao_texture( + self.material, bpy.context + ) + MSFS_Material_Property_Update.update_dirt_texture(self.material, bpy.context) + MSFS_Material_Property_Update.update_wiper_mask(self.material, bpy.context) + MSFS_Material_Property_Update.update_alpha_mode(self.material, bpy.context) + MSFS_Material_Property_Update.update_emissive_scale(self.material, bpy.context) + MSFS_Material_Property_Update.update_normal_scale(self.material, bpy.context) + MSFS_Material_Property_Update.update_color_sss(self.material, bpy.context) + MSFS_Material_Property_Update.update_double_sided(self.material, bpy.context) + MSFS_Material_Property_Update.update_alpha_cutoff(self.material, bpy.context) + MSFS_Material_Property_Update.update_detail_uv(self.material, bpy.context) + # Trigger setters + self.material.msfs_base_color_factor = self.material.msfs_base_color_factor + self.material.msfs_emissive_factor = self.material.msfs_emissive_factor + self.material.msfs_metallic_factor = self.material.msfs_metallic_factor + self.material.msfs_roughness_factor = self.material.msfs_roughness_factor + self.material.msfs_base_color_factor = self.material.msfs_base_color_factor + def cleanNodeTree(self): nodes = self.material.node_tree.nodes diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py index 30e066a..7bcdb1d 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py @@ -16,6 +16,8 @@ import bpy +from .msfs_material_prop_update import MSFS_Material_Property_Update + class MSFS_OT_MigrateMaterialData(bpy.types.Operator): # TODO: Remove eventually """This addon changes some of the internal property names. This current material has older properties, and is able to be migrated.\nWARNING: This removes all the old properties from the material""" @@ -25,7 +27,6 @@ class MSFS_OT_MigrateMaterialData(bpy.types.Operator): # TODO: Remove eventually old_property_to_new_mapping = { "msfs_color_albedo_mix": "msfs_base_color_factor", - "msfs_color_emissive_mix": "msfs_emissive_factor", "msfs_color_sss": "msfs_sss_color", "msfs_use_pearl_effect ": "msfs_use_pearl", "msfs_decal_blend_factor_color": "msfs_base_color_blend_factor", @@ -80,6 +81,10 @@ def execute(self, context): del mat[old_property] + # Emissive factor is a special case - old material system had 4 floats, we only need 3 + if mat.get("msfs_color_emissive_mix"): + mat.msfs_emissive_factor = mat.get("msfs_color_emissive_mix")[0:3] + # Do our enums manually as only their index of the value are stored - not the string if mat.get("msfs_blend_mode"): old_alpha_order = [ @@ -92,7 +97,6 @@ def execute(self, context): del mat["msfs_blend_mode"] - # Do material type last so that the material update call overwrites any values that should change based off material (old addon didn't implement this) if mat.get("msfs_material_mode"): old_material_older = [ # Assuming the user uninstalled the old plugin, the index of the value will be stored instead of the name of the current material. Replicate the order here "NONE", @@ -116,6 +120,8 @@ def execute(self, context): del mat["msfs_material_mode"] + MSFS_Material_Property_Update.update_msfs_material_type(mat, context) + return {"FINISHED"} From e9a3934750447bfd0621060208214773333dda03 Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Tue, 15 Mar 2022 10:56:06 -0400 Subject: [PATCH 06/31] feat: add material animations (#33) --- .../blender/msfs_material_panel.py | 5 +- .../blender/msfs_material_prop_update.py | 68 ++- .../com/msfs_material_props.py | 12 +- addons/io_scene_gltf2_msfs/io/msfs_export.py | 27 + addons/io_scene_gltf2_msfs/io/msfs_import.py | 5 + .../io/msfs_material_animation.py | 496 ++++++++++++++++++ 6 files changed, 587 insertions(+), 26 deletions(-) create mode 100644 addons/io_scene_gltf2_msfs/io/msfs_material_animation.py diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py index 7bcdb1d..e1e57cb 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py @@ -157,9 +157,8 @@ def draw_texture_prop(self, layout, mat, prop, enabled=True, visible=True, text= def draw(self, context): layout = self.layout - # Disabled animation UI until material animations are properly implemented - # layout.use_property_split = True - # layout.use_property_decorate = True + layout.use_property_split = True + layout.use_property_decorate = True mat = context.active_object.active_material diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py index 3032e81..4b8fe35 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py @@ -256,22 +256,62 @@ def update_alpha_mode(self, context): msfs_mat = MSFS_Material(self) msfs_mat.setBlendMode(self.msfs_alpha_mode) - # Update functions for the "tint" parameters: + # Getters/setters for animatable properties + # The reason we need these instead of update callbacks is because we want to make sure the properties update when scrolling through + # the animation timeline. Update is not called unless the value is manually changed, while get/set is called anytime the value is + # internally updated, such as in the timeline + @staticmethod - def update_base_color(self, context): - msfs = MSFS_Material_Property_Update.getMaterial(self) - msfs.setBaseColor(self.msfs_base_color_factor) + def get_base_color(self): + return self.get("msfs_base_color_factor", [1.0, 1.0, 1.0, 1.0]) @staticmethod - def update_emissive_color(self, context): - nodes = self.node_tree.nodes + def set_base_color(self, value): + msfs = MSFS_Material_Property_Update.getMaterial(self.id_data) + msfs.setBaseColor(value) + + self["msfs_base_color_factor"] = value + + @staticmethod + def get_emissive_color(self): + return self.get("msfs_emissive_factor", [0.0, 0.0, 0.0]) + + @staticmethod + def set_emissive_color(self, value): + nodes = self.id_data.node_tree.nodes nodeEmissiveColorRGB = nodes.get(MSFS_ShaderNodes.emissiveColor.value) if not nodeEmissiveColorRGB: return emissiveValue = nodeEmissiveColorRGB.outputs[0].default_value - emissiveValue[0] = self.msfs_emissive_factor[0] - emissiveValue[1] = self.msfs_emissive_factor[1] - emissiveValue[2] = self.msfs_emissive_factor[2] + emissiveValue[0] = value[0] + emissiveValue[1] = value[1] + emissiveValue[2] = value[2] + + self["msfs_emissive_factor"] = value + + @staticmethod + def get_metallic_factor(self): + return self.get("msfs_metallic_factor", 1.0) + + @staticmethod + def set_metallic_factor(self, value): + msfs = MSFS_Material_Property_Update.getMaterial(self.id_data) + msfs.setMetallicScale(value) + + self["msfs_metallic_factor"] = value + + @staticmethod + def get_roughness_factor(self): + return self.get("msfs_roughness_factor", 1.0) + + @staticmethod + def set_roughness_factor(self, value): + msfs = MSFS_Material_Property_Update.getMaterial(self.id_data) + msfs.setRoughnessScale(value) + + self["msfs_roughness_factor"] = value + + # Regular update functions @staticmethod def update_emissive_scale(self, context): @@ -281,16 +321,6 @@ def update_emissive_scale(self, context): return emissiveScale.outputs[0].default_value = self.msfs_emissive_scale - @staticmethod - def update_metallic_scale(self, context): - msfs = MSFS_Material_Property_Update.getMaterial(self) - msfs.setMetallicScale(self.msfs_metallic_factor) - - @staticmethod - def update_roughness_scale(self, context): - msfs = MSFS_Material_Property_Update.getMaterial(self) - msfs.setRoughnessScale(self.msfs_roughness_factor) - @staticmethod def update_normal_scale(self, context): msfs = MSFS_Material_Property_Update.getMaterial(self) diff --git a/addons/io_scene_gltf2_msfs/com/msfs_material_props.py b/addons/io_scene_gltf2_msfs/com/msfs_material_props.py index d4c404b..2803b0e 100644 --- a/addons/io_scene_gltf2_msfs/com/msfs_material_props.py +++ b/addons/io_scene_gltf2_msfs/com/msfs_material_props.py @@ -65,7 +65,8 @@ class Defaults: max=1.0, size=4, default=Defaults.BaseColorFactor, - update=MSFS_Material_Property_Update.update_base_color, + get=MSFS_Material_Property_Update.get_base_color, + set=MSFS_Material_Property_Update.set_base_color, options={"ANIMATABLE"}, ) bpy.types.Material.msfs_emissive_factor = bpy.props.FloatVectorProperty( @@ -76,7 +77,8 @@ class Defaults: max=1.0, size=3, default=Defaults.EmissiveFactor, - update=MSFS_Material_Property_Update.update_emissive_color, + get=MSFS_Material_Property_Update.get_emissive_color, + set=MSFS_Material_Property_Update.set_emissive_color, options={"ANIMATABLE"}, ) bpy.types.Material.msfs_metallic_factor = bpy.props.FloatProperty( @@ -85,7 +87,8 @@ class Defaults: min=0.0, max=1.0, default=Defaults.MetallicFactor, - update=MSFS_Material_Property_Update.update_metallic_scale, + get=MSFS_Material_Property_Update.get_metallic_factor, + set=MSFS_Material_Property_Update.set_metallic_factor, options={"ANIMATABLE"}, ) bpy.types.Material.msfs_roughness_factor = bpy.props.FloatProperty( @@ -94,7 +97,8 @@ class Defaults: min=0.0, max=1.0, default=Defaults.RoughnessFactor, - update=MSFS_Material_Property_Update.update_roughness_scale, + get=MSFS_Material_Property_Update.get_roughness_factor, + set=MSFS_Material_Property_Update.set_roughness_factor, options={"ANIMATABLE"}, ) bpy.types.Material.msfs_normal_scale = bpy.props.FloatProperty( diff --git a/addons/io_scene_gltf2_msfs/io/msfs_export.py b/addons/io_scene_gltf2_msfs/io/msfs_export.py index f922c18..2f2b513 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_export.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_export.py @@ -23,10 +23,12 @@ from .msfs_light import MSFSLight from .msfs_gizmo import MSFSGizmo from .msfs_material import MSFSMaterial +from .msfs_material_animation import MSFSMaterialAnimation class Export: gizmoNodes = None + material_actions = [] def gather_asset_hook(self, gltf2_asset, export_settings): if self.properties.enabled == True: @@ -46,6 +48,9 @@ def gather_gltf_extensions_hook(self, gltf2_plan, export_settings): for i, image in enumerate(gltf2_plan.images): image.uri = os.path.basename(urllib.parse.unquote(image.uri)) + for animation in gltf2_plan.animations: + MSFSMaterialAnimation.finalize_target(animation, gltf2_plan) + def gather_node_hook(self, gltf2_object, blender_object, export_settings): if self.properties.enabled: if blender_object.msfs_gizmo_type != "NONE": @@ -84,3 +89,25 @@ def get_children(node): def gather_material_hook(self, gltf2_material, blender_material, export_settings): if self.properties.enabled: MSFSMaterial.export(gltf2_material, blender_material, export_settings) + + def gather_actions_hook(self, blender_object, blender_actions, blender_tracks, action_on_type, export_settings): + if self.properties.enabled: + # Keep track of what material actions we've already exported - no need to export it more than once. All values passed to the hook get modified by reference + found_blender_actions, found_blender_tracks, found_action_on_type = MSFSMaterialAnimation.gather_actions(blender_object, self.material_actions, export_settings) + + if found_blender_actions: + blender_actions.extend(found_blender_actions) + self.material_actions.extend(found_blender_actions) + if found_blender_tracks: + blender_tracks.update(found_blender_tracks) + if found_action_on_type: + action_on_type.update(found_action_on_type) + + def gather_animation_channel_target_hook(self, gltf2_animation_channel_target, channels, blender_object, bake_bone, bake_channel, export_settings): + MSFSMaterialAnimation.replace_channel_target(gltf2_animation_channel_target, channels, blender_object, export_settings) + + def pre_gather_animation_hook(self, gltf2_animation, blender_action, blender_object, export_settings): + MSFSMaterialAnimation.add_placeholder_channel(gltf2_animation, blender_action, blender_object, export_settings) + + def gather_animation_hook(self, gltf2_animation, blender_action, blender_object, export_settings): + MSFSMaterialAnimation.finalize_animation(gltf2_animation) \ No newline at end of file diff --git a/addons/io_scene_gltf2_msfs/io/msfs_import.py b/addons/io_scene_gltf2_msfs/io/msfs_import.py index 0eee229..da1e540 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_import.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_import.py @@ -18,6 +18,7 @@ from .msfs_light import MSFSLight from .msfs_gizmo import MSFSGizmo from .msfs_material import MSFSMaterial +from .msfs_material_animation import MSFSMaterialAnimation class Import: @@ -35,3 +36,7 @@ def gather_import_node_after_hook(self, vnode, gltf2_node, blender_object, impor # Create materials def gather_import_material_after_hook(self, gltf2_material, vertex_color, blender_material, import_settings): MSFSMaterial.create(gltf2_material, blender_material, import_settings) + + # Create material animations + def gather_import_scene_after_nodes_hook(self, gltf2_scene, blender_scene, import_settings): # TODO: move this to proper animation hook once added + MSFSMaterialAnimation.create(import_settings) diff --git a/addons/io_scene_gltf2_msfs/io/msfs_material_animation.py b/addons/io_scene_gltf2_msfs/io/msfs_material_animation.py new file mode 100644 index 0000000..4699643 --- /dev/null +++ b/addons/io_scene_gltf2_msfs/io/msfs_material_animation.py @@ -0,0 +1,496 @@ +# glTF-Blender-IO-MSFS +# Copyright (C) 2022 The glTF-Blender-IO-MSFS authors + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import bpy +import numpy as np + +from io_scene_gltf2.io.imp.gltf2_io_binary import BinaryData +from io_scene_gltf2.blender.imp.gltf2_blender_animation_utils import ( + make_fcurve, + simulate_stash, +) +from io_scene_gltf2.blender.exp import gltf2_blender_gather_animation_channels +from io_scene_gltf2.io.com.gltf2_io_extensions import Extension + + +class MSFSMaterialAnimationTarget: + """ + Utility class to help us set the final material animation target. We lose these values during the export process, as the data path needs to be + set to 'value' during export in order to get the Khronos exporter to pick up on the animation. The reference to the material is important to + find the final glTF material, and channels is used to restore the original data path at the end of export. + + :param material: a blender material + :param data_path: string value that contains the prop name + :param channels: list of blender fcurves in order for us to restore data path after export + """ + + def __init__(self, material, data_path, channels): + self.material = material + self.data_path = data_path + self.channels = channels + + +class MSFSMaterialAnimation: + bl_options = {"UNDO"} + + extension_name = "ASOBO_property_animation" + + def __new__(cls, *args, **kwargs): + raise RuntimeError("%s should not be instantiated" % cls) + + @staticmethod + def create(import_settings): + # NLA tracks are added bottom to top, so create animations in reverse so the first winds up on top + for gltf2_animation in reversed(import_settings.data.animations): + import_settings.action_cache = {} + import_settings.needs_stash = [] + + if gltf2_animation.extensions is None: + return + + extension = gltf2_animation.extensions.get( + MSFSMaterialAnimation.extension_name + ) + if extension is None: + return + + for channel in extension.get("channels"): + sampler = channel.get("sampler") + target = channel.get("target") + + material_idx = int( + target.split("/")[1] + ) # The target will look something like "materials/INDEX/PROPERTY", so we use this to get the material index + path = target.replace( + f"materials/{material_idx}/", "" + ) # Get the actual path from the target - we have to remove the "materials/INDEX/" prefix from the path to get the path + + action = MSFSMaterialAnimation.get_or_create_action( + import_settings, material_idx, gltf2_animation.track_name + ) + + keys = BinaryData.get_data_from_accessor( + import_settings, gltf2_animation.samplers[sampler].input + ) + values = BinaryData.get_data_from_accessor( + import_settings, gltf2_animation.samplers[sampler].output + ) + + if gltf2_animation.samplers[sampler].interpolation == "CUBICSPLINE": + values = values[1::3] + + blender_path = None + group_name = None + num_components = 0 + if path == "pbrMetallicRoughness/baseColorFactor": + blender_path = "msfs_base_color_factor" + group_name = "Base Color" + num_components = 4 + elif path == "emissiveFactor": + blender_path = "msfs_emissive_factor" + group_name = "Emissive Color" + num_components = 3 + elif path == "pbrMetallicRoughness/metallicFactor": + blender_path = "msfs_metallic_factor" + group_name = "Metallic Factor" + num_components = 1 + elif path == "pbrMetallicRoughness/roughnessFactor": + blender_path = "msfs_roughness_factor" + group_name = "Roughness Factor" + num_components = 1 + elif path == "extensions/ASOBO_material_UV_options/UVOffsetU": + blender_path = "msfs_uv_offset_u" + group_name = "UV Offset U" + num_components = 1 + elif path == "extensions/ASOBO_material_UV_options/UVOffsetV": + blender_path = "msfs_uv_offset_v" + group_name = "UV Offset V" + num_components = 1 + elif path == "extensions/ASOBO_material_UV_options/UVTilingU": + blender_path = "msfs_uv_tiling_u" + group_name = "UV Tiling U" + num_components = 1 + elif path == "extensions/ASOBO_material_UV_options/UVTilingV": + blender_path = "msfs_uv_tiling_v" + group_name = "UV Tiling V" + num_components = 1 + elif path == "extensions/ASOBO_material_UV_options/UVRotation": + blender_path = "msfs_uv_rotation" + group_name = "UV Rotation" + num_components = 1 + elif path == "extensions/ASOBO_material_windshield_v2/wiper1State": + blender_path = "msfs_wiper_1_state" + group_name = "Wiper 1 State" + num_components = 1 + elif path == "extensions/ASOBO_material_windshield_v2/wiper2State": + blender_path = "msfs_wiper_2_state" + group_name = "Wiper 2 State" + num_components = 1 + elif path == "extensions/ASOBO_material_windshield_v2/wiper3State": + blender_path = "msfs_wiper_3_state" + group_name = "Wiper 3 State" + num_components = 1 + elif path == "extensions/ASOBO_material_windshield_v2/wiper4State": + blender_path = "msfs_wiper_4_state" + group_name = "Wiper 4 State" + num_components = 1 + + # Group values by component size + values = np.array_split(values, len(values) / num_components) + + # Create action + fps = bpy.context.scene.render.fps + + coords = [0] * (2 * len(keys)) + coords[::2] = (key[0] * fps for key in keys) + + for i in range(0, num_components): + coords[1::2] = (vals[i] for vals in values) + make_fcurve( + action, + coords, + data_path=blender_path, + index=i, + group_name=group_name, + interpolation=gltf2_animation.samplers[sampler].interpolation, + ) + + # Push all actions onto NLA tracks + for (mat, action) in import_settings.needs_stash: + simulate_stash(mat, gltf2_animation.track_name, action) + + # Unmute track TODO: not sure why it gets muted - look into this + for track in mat.animation_data.nla_tracks: + if track.name == gltf2_animation.track_name: + track.mute = False + + @staticmethod + def get_or_create_action(gltf, material_idx, anim_name): + """ + IMPORT + Utility function to create or get a blender action on a material. If the action already exists, we return instead of creating + + :param gltf: the glTF import plan + :param material_idx: index of glTF material + :param anim_name: name of animation we are importing + """ + mat = gltf.data.materials[material_idx] + mat_name = list(mat.blender_material.values())[ + 0 + ] # The blender_material dictionary will only have one key-value pair, so we can get the value of the first item, which will be the blender material name + + blender_mat = bpy.data.materials[mat_name] + + action = gltf.action_cache.get(mat_name) + if not action: + name = anim_name + "_" + mat_name + action = bpy.data.actions.new(name) + action.id_root = "MATERIAL" + gltf.needs_stash.append((blender_mat, action)) + gltf.action_cache[mat_name] = action + + return action + + @staticmethod + def get_material_from_action(blender_object, blender_action, export_settings): + """ + EXPORT + Utility function to return a blender material from an action, if the action is a material + + :param blender_object: the blender object that is being animated + :param blender_action: the blender action that is being exported + + :return: a blender material, or None + """ + for material_slot in blender_object.material_slots: + material = material_slot.material + + if material is None: + continue + + if material.animation_data is not None: + if blender_action == material.animation_data.action: + return material + elif ( + export_settings["gltf_nla_strips"] is True + ): # Check if the animation is an NLA strip + for track in material.animation_data.nla_tracks: + non_muted_strips = [ + strip + for strip in track.strips + if strip.action is not None and strip.mute is False + ] + if track.strips is None or len(non_muted_strips) != 1: + continue + for strip in non_muted_strips: + if blender_action == strip.action: + return material + + @staticmethod + def gather_actions( + blender_object, gathered_actions, export_settings + ): + """ + EXPORT + Based off code in the Khronos glTF exporter. This looks through all the materials in the object and checks + if there are any animations on them, and if so, add to the actions list. + + :param blender_object: the blender object that is being animated + :param gathered_actions: list of blender actions already gathered + :return: blender_actions, blender_tracks, action_on_type + """ + blender_actions = [] + blender_tracks = {} + action_on_type = {} + + # First step is to get a list of all material animation actions and NLA tracks (if used) + if not ( + blender_object.type == "MESH" + and blender_object.data is not None + and len(blender_object.material_slots) > 0 + ): + return blender_actions, blender_tracks, action_on_type + + for material_slot in blender_object.material_slots: + material = material_slot.material + + if material is None or material.animation_data is None: + continue + + if material.animation_data.action is not None and material.animation_data.action not in gathered_actions: # If more than one object shares this material, the action will get exported multiple times. We prevent that by checking if we've already gathered it + blender_actions.append(material.animation_data.action) + blender_tracks[material.animation_data.action.name] = None + action_on_type[material.animation_data.action.name] = "MATERIAL" + + # Collect associated strips from NLA tracks + if export_settings["gltf_nla_strips"] is True: + for track in material.animation_data.nla_tracks: + # Multi-strip tracks do not export correctly yet (they need to be baked), + # so skip them for now and only write single-strip tracks. + non_muted_strips = [ + strip + for strip in track.strips + if strip.action is not None and strip.mute is False + ] + if track.strips is None or len(non_muted_strips) != 1: + continue + for strip in non_muted_strips: + if strip.action not in gathered_actions: + blender_actions.append(strip.action) + blender_tracks[ + strip.action.name + ] = ( + track.name + ) # Always set after possible active action -> None will be overwrite + action_on_type[strip.action.name] = "MATERIAL" + + return blender_actions, blender_tracks, action_on_type + + @staticmethod + def replace_channel_target( + gltf2_animation_channel_target, channels, blender_object, export_settings + ): + """ + EXPORT + Replace targets for channels that are material animations. We don't use the target object for material targets, instead we + have a path to the material index and cooresponding property. Unfortunately, we don't have access to the finalized glTF material tree yet, + so we need to temporarily keep a reference to the blender material and the value that is being animated. This is properly finalized later. + + :param gltf2_animation_channel_target: a glTF animation channel target + :param channels: list of channel groups gathered by the Khronos exporter. This has the data_path that we're interested in. + :param blender_object: the blender object that is being animated + :param action_name: the name of the blender action being exported + :return: + """ + for channel in channels: + blender_material = MSFSMaterialAnimation.get_material_from_action( + blender_object, channel.id_data, export_settings + ) + if not blender_material: + continue + + gltf2_animation_channel_target.path = MSFSMaterialAnimationTarget( + blender_material, channel.data_path, channels + ) + + # Temporarily set data path to value in order to force the Khronos exporter to gather keyframes. We undo this later in the export process + channel.data_path = "value" + + @staticmethod + def add_placeholder_channel( + gltf2_animation, blender_action, blender_object, export_settings + ): + """ + EXPORT + If we have a glTF animation with only material animations, we need to create a placeholder scale channel. Because we utilize the `extensions` + attribute of the animation, the channels end up being empty, which is against the glTF spec. By using this fake scale channel, we bypass this issue. + + :param gltf2_animation: a glTF animation + :param blender_action: the blender action that is being exported + :param blender_object: the blender object that is being animated + :param export_settings: dictionary of export settings provided by the Khronos exporter + :return: + """ + if not blender_action.fcurves: + return + + for fcurve in blender_action.fcurves: + material = MSFSMaterialAnimation.get_material_from_action( + blender_object, blender_action, export_settings + ) + + if material is None: + # If we actually find a property besides the material animations, we don't need a temp fcurve + return + + # Create temp action and insert fake keyframes + temp_action = bpy.data.actions.new(name="TempAction") + + fcurve = temp_action.fcurves.new(data_path="scale", index=0) + fcurve.keyframe_points.add(1) + + # Collect temp channel and cleanup + gltf2_animation.channels.extend( + gltf2_blender_gather_animation_channels.gather_animation_channels( + temp_action, blender_object, export_settings + ) + ) + + bpy.data.actions.remove(temp_action) + + @staticmethod + def finalize_animation(gltf2_animation): + """ + EXPORT + After the glTF animation is done being gathered, we can move all material animated channels to the Asobo extension and remove it from `channels`. + + :param gltf2_animation: a glTF animation + :return: + """ + material_animation_channels = [] + for channel in list(gltf2_animation.channels): + if not isinstance(channel.target.path, MSFSMaterialAnimationTarget): + continue + + if type(channel.target.path.material) != bpy.types.Material: + continue + + material_animation_channels.append( + {"sampler": channel.sampler, "target": channel.target.path} + ) + + # Restore proper animation channel paths + for blender_channel in channel.target.path.channels: + blender_channel.data_path = channel.target.path.data_path + + gltf2_animation.channels.remove(channel) + + if material_animation_channels: + gltf2_animation.extensions[ + MSFSMaterialAnimation.extension_name + ] = Extension( + name=MSFSMaterialAnimation.extension_name, + extension={"channels": material_animation_channels}, + required=False, + ) + + @staticmethod + def finalize_target(gltf2_animation, gltf2_plan): + """ + EXPORT + Now that we have the finalized material tree, we can properly set the animation channel targets to the proper index, and replace the temporary + blender material reference. + + :param gltf2_animation: a glTF animation + :param gltf2_plan: the finalized glTF data + :return: + """ + if not gltf2_animation.extensions: + return + + if ( + MSFSMaterialAnimation.extension_name + not in gltf2_animation.extensions.keys() + ): + return + + for channel in list(gltf2_animation.extensions[MSFSMaterialAnimation.extension_name][ + "channels" + ]): + material_index = None + for j, material in enumerate(gltf2_plan.materials): + if material.name == channel["target"].material.name: + material_index = j + break + + if material_index is None: + continue + + target_property = channel["target"].data_path + + if target_property == "msfs_base_color_factor": + channel[ + "target" + ] = f"materials/{material_index}/pbrMetallicRoughness/baseColorFactor" + elif target_property == "msfs_emissive_factor": + channel["target"] = f"materials/{material_index}/emissiveFactor" + elif target_property == "msfs_metallic_factor": + channel[ + "target" + ] = f"materials/{material_index}/pbrMetallicRoughness/metallicFactor" + elif target_property == "msfs_roughness_factor": + channel[ + "target" + ] = f"materials/{material_index}/pbrMetallicRoughness/roughnessFactor" + elif target_property == "msfs_uv_offset_u": + channel[ + "target" + ] = f"materials/{material_index}/extensions/ASOBO_material_UV_options/UVOffsetU" + elif target_property == "msfs_uv_offset_v": + channel[ + "target" + ] = f"materials/{material_index}/extensions/ASOBO_material_UV_options/UVOffsetV" + elif target_property == "msfs_uv_tiling_u": + channel[ + "target" + ] = f"materials/{material_index}/extensions/ASOBO_material_UV_options/UVTilingU" + elif target_property == "msfs_uv_tiling_v": + channel[ + "target" + ] = f"materials/{material_index}/extensions/ASOBO_material_UV_options/UVTilingV" + elif target_property == "msfs_uv_rotation": + channel[ + "target" + ] = f"materials/{material_index}/extensions/ASOBO_material_UV_options/UVRotation" + elif target_property == "msfs_wiper_1_state": + channel[ + "target" + ] = f"materials/{material_index}/extensions/ASOBO_material_windshield_v2/wiper1State" + elif target_property == "msfs_wiper_2_state": + channel[ + "target" + ] = f"materials/{material_index}/extensions/ASOBO_material_windshield_v2/wiper2State" + elif target_property == "msfs_wiper_3_state": + channel[ + "target" + ] = f"materials/{material_index}/extensions/ASOBO_material_windshield_v2/wiper3State" + elif target_property == "msfs_wiper_4_state": + channel[ + "target" + ] = f"materials/{material_index}/extensions/ASOBO_material_windshield_v2/wiper4State" + else: + # If we somehow have a property animated that isn't supported, remove the channel + gltf2_animation.extensions[MSFSMaterialAnimation.extension_name]["channels"].remove(channel) From e4f37855f6a3090f632e9b681537fa9b8bf804eb Mon Sep 17 00:00:00 2001 From: lpierabella <77288191+lpierabella@users.noreply.github.com> Date: Tue, 15 Mar 2022 16:00:03 +0100 Subject: [PATCH 07/31] Revert "feat: add material animations (#33)" This reverts commit e9a3934750447bfd0621060208214773333dda03. --- .../blender/msfs_material_panel.py | 5 +- .../blender/msfs_material_prop_update.py | 68 +-- .../com/msfs_material_props.py | 12 +- addons/io_scene_gltf2_msfs/io/msfs_export.py | 27 - addons/io_scene_gltf2_msfs/io/msfs_import.py | 5 - .../io/msfs_material_animation.py | 496 ------------------ 6 files changed, 26 insertions(+), 587 deletions(-) delete mode 100644 addons/io_scene_gltf2_msfs/io/msfs_material_animation.py diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py index e1e57cb..7bcdb1d 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py @@ -157,8 +157,9 @@ def draw_texture_prop(self, layout, mat, prop, enabled=True, visible=True, text= def draw(self, context): layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = True + # Disabled animation UI until material animations are properly implemented + # layout.use_property_split = True + # layout.use_property_decorate = True mat = context.active_object.active_material diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py index 4b8fe35..3032e81 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py @@ -256,62 +256,22 @@ def update_alpha_mode(self, context): msfs_mat = MSFS_Material(self) msfs_mat.setBlendMode(self.msfs_alpha_mode) - # Getters/setters for animatable properties - # The reason we need these instead of update callbacks is because we want to make sure the properties update when scrolling through - # the animation timeline. Update is not called unless the value is manually changed, while get/set is called anytime the value is - # internally updated, such as in the timeline - - @staticmethod - def get_base_color(self): - return self.get("msfs_base_color_factor", [1.0, 1.0, 1.0, 1.0]) - + # Update functions for the "tint" parameters: @staticmethod - def set_base_color(self, value): - msfs = MSFS_Material_Property_Update.getMaterial(self.id_data) - msfs.setBaseColor(value) - - self["msfs_base_color_factor"] = value - - @staticmethod - def get_emissive_color(self): - return self.get("msfs_emissive_factor", [0.0, 0.0, 0.0]) + def update_base_color(self, context): + msfs = MSFS_Material_Property_Update.getMaterial(self) + msfs.setBaseColor(self.msfs_base_color_factor) @staticmethod - def set_emissive_color(self, value): - nodes = self.id_data.node_tree.nodes + def update_emissive_color(self, context): + nodes = self.node_tree.nodes nodeEmissiveColorRGB = nodes.get(MSFS_ShaderNodes.emissiveColor.value) if not nodeEmissiveColorRGB: return emissiveValue = nodeEmissiveColorRGB.outputs[0].default_value - emissiveValue[0] = value[0] - emissiveValue[1] = value[1] - emissiveValue[2] = value[2] - - self["msfs_emissive_factor"] = value - - @staticmethod - def get_metallic_factor(self): - return self.get("msfs_metallic_factor", 1.0) - - @staticmethod - def set_metallic_factor(self, value): - msfs = MSFS_Material_Property_Update.getMaterial(self.id_data) - msfs.setMetallicScale(value) - - self["msfs_metallic_factor"] = value - - @staticmethod - def get_roughness_factor(self): - return self.get("msfs_roughness_factor", 1.0) - - @staticmethod - def set_roughness_factor(self, value): - msfs = MSFS_Material_Property_Update.getMaterial(self.id_data) - msfs.setRoughnessScale(value) - - self["msfs_roughness_factor"] = value - - # Regular update functions + emissiveValue[0] = self.msfs_emissive_factor[0] + emissiveValue[1] = self.msfs_emissive_factor[1] + emissiveValue[2] = self.msfs_emissive_factor[2] @staticmethod def update_emissive_scale(self, context): @@ -321,6 +281,16 @@ def update_emissive_scale(self, context): return emissiveScale.outputs[0].default_value = self.msfs_emissive_scale + @staticmethod + def update_metallic_scale(self, context): + msfs = MSFS_Material_Property_Update.getMaterial(self) + msfs.setMetallicScale(self.msfs_metallic_factor) + + @staticmethod + def update_roughness_scale(self, context): + msfs = MSFS_Material_Property_Update.getMaterial(self) + msfs.setRoughnessScale(self.msfs_roughness_factor) + @staticmethod def update_normal_scale(self, context): msfs = MSFS_Material_Property_Update.getMaterial(self) diff --git a/addons/io_scene_gltf2_msfs/com/msfs_material_props.py b/addons/io_scene_gltf2_msfs/com/msfs_material_props.py index 2803b0e..d4c404b 100644 --- a/addons/io_scene_gltf2_msfs/com/msfs_material_props.py +++ b/addons/io_scene_gltf2_msfs/com/msfs_material_props.py @@ -65,8 +65,7 @@ class Defaults: max=1.0, size=4, default=Defaults.BaseColorFactor, - get=MSFS_Material_Property_Update.get_base_color, - set=MSFS_Material_Property_Update.set_base_color, + update=MSFS_Material_Property_Update.update_base_color, options={"ANIMATABLE"}, ) bpy.types.Material.msfs_emissive_factor = bpy.props.FloatVectorProperty( @@ -77,8 +76,7 @@ class Defaults: max=1.0, size=3, default=Defaults.EmissiveFactor, - get=MSFS_Material_Property_Update.get_emissive_color, - set=MSFS_Material_Property_Update.set_emissive_color, + update=MSFS_Material_Property_Update.update_emissive_color, options={"ANIMATABLE"}, ) bpy.types.Material.msfs_metallic_factor = bpy.props.FloatProperty( @@ -87,8 +85,7 @@ class Defaults: min=0.0, max=1.0, default=Defaults.MetallicFactor, - get=MSFS_Material_Property_Update.get_metallic_factor, - set=MSFS_Material_Property_Update.set_metallic_factor, + update=MSFS_Material_Property_Update.update_metallic_scale, options={"ANIMATABLE"}, ) bpy.types.Material.msfs_roughness_factor = bpy.props.FloatProperty( @@ -97,8 +94,7 @@ class Defaults: min=0.0, max=1.0, default=Defaults.RoughnessFactor, - get=MSFS_Material_Property_Update.get_roughness_factor, - set=MSFS_Material_Property_Update.set_roughness_factor, + update=MSFS_Material_Property_Update.update_roughness_scale, options={"ANIMATABLE"}, ) bpy.types.Material.msfs_normal_scale = bpy.props.FloatProperty( diff --git a/addons/io_scene_gltf2_msfs/io/msfs_export.py b/addons/io_scene_gltf2_msfs/io/msfs_export.py index 2f2b513..f922c18 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_export.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_export.py @@ -23,12 +23,10 @@ from .msfs_light import MSFSLight from .msfs_gizmo import MSFSGizmo from .msfs_material import MSFSMaterial -from .msfs_material_animation import MSFSMaterialAnimation class Export: gizmoNodes = None - material_actions = [] def gather_asset_hook(self, gltf2_asset, export_settings): if self.properties.enabled == True: @@ -48,9 +46,6 @@ def gather_gltf_extensions_hook(self, gltf2_plan, export_settings): for i, image in enumerate(gltf2_plan.images): image.uri = os.path.basename(urllib.parse.unquote(image.uri)) - for animation in gltf2_plan.animations: - MSFSMaterialAnimation.finalize_target(animation, gltf2_plan) - def gather_node_hook(self, gltf2_object, blender_object, export_settings): if self.properties.enabled: if blender_object.msfs_gizmo_type != "NONE": @@ -89,25 +84,3 @@ def get_children(node): def gather_material_hook(self, gltf2_material, blender_material, export_settings): if self.properties.enabled: MSFSMaterial.export(gltf2_material, blender_material, export_settings) - - def gather_actions_hook(self, blender_object, blender_actions, blender_tracks, action_on_type, export_settings): - if self.properties.enabled: - # Keep track of what material actions we've already exported - no need to export it more than once. All values passed to the hook get modified by reference - found_blender_actions, found_blender_tracks, found_action_on_type = MSFSMaterialAnimation.gather_actions(blender_object, self.material_actions, export_settings) - - if found_blender_actions: - blender_actions.extend(found_blender_actions) - self.material_actions.extend(found_blender_actions) - if found_blender_tracks: - blender_tracks.update(found_blender_tracks) - if found_action_on_type: - action_on_type.update(found_action_on_type) - - def gather_animation_channel_target_hook(self, gltf2_animation_channel_target, channels, blender_object, bake_bone, bake_channel, export_settings): - MSFSMaterialAnimation.replace_channel_target(gltf2_animation_channel_target, channels, blender_object, export_settings) - - def pre_gather_animation_hook(self, gltf2_animation, blender_action, blender_object, export_settings): - MSFSMaterialAnimation.add_placeholder_channel(gltf2_animation, blender_action, blender_object, export_settings) - - def gather_animation_hook(self, gltf2_animation, blender_action, blender_object, export_settings): - MSFSMaterialAnimation.finalize_animation(gltf2_animation) \ No newline at end of file diff --git a/addons/io_scene_gltf2_msfs/io/msfs_import.py b/addons/io_scene_gltf2_msfs/io/msfs_import.py index da1e540..0eee229 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_import.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_import.py @@ -18,7 +18,6 @@ from .msfs_light import MSFSLight from .msfs_gizmo import MSFSGizmo from .msfs_material import MSFSMaterial -from .msfs_material_animation import MSFSMaterialAnimation class Import: @@ -36,7 +35,3 @@ def gather_import_node_after_hook(self, vnode, gltf2_node, blender_object, impor # Create materials def gather_import_material_after_hook(self, gltf2_material, vertex_color, blender_material, import_settings): MSFSMaterial.create(gltf2_material, blender_material, import_settings) - - # Create material animations - def gather_import_scene_after_nodes_hook(self, gltf2_scene, blender_scene, import_settings): # TODO: move this to proper animation hook once added - MSFSMaterialAnimation.create(import_settings) diff --git a/addons/io_scene_gltf2_msfs/io/msfs_material_animation.py b/addons/io_scene_gltf2_msfs/io/msfs_material_animation.py deleted file mode 100644 index 4699643..0000000 --- a/addons/io_scene_gltf2_msfs/io/msfs_material_animation.py +++ /dev/null @@ -1,496 +0,0 @@ -# glTF-Blender-IO-MSFS -# Copyright (C) 2022 The glTF-Blender-IO-MSFS authors - -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import bpy -import numpy as np - -from io_scene_gltf2.io.imp.gltf2_io_binary import BinaryData -from io_scene_gltf2.blender.imp.gltf2_blender_animation_utils import ( - make_fcurve, - simulate_stash, -) -from io_scene_gltf2.blender.exp import gltf2_blender_gather_animation_channels -from io_scene_gltf2.io.com.gltf2_io_extensions import Extension - - -class MSFSMaterialAnimationTarget: - """ - Utility class to help us set the final material animation target. We lose these values during the export process, as the data path needs to be - set to 'value' during export in order to get the Khronos exporter to pick up on the animation. The reference to the material is important to - find the final glTF material, and channels is used to restore the original data path at the end of export. - - :param material: a blender material - :param data_path: string value that contains the prop name - :param channels: list of blender fcurves in order for us to restore data path after export - """ - - def __init__(self, material, data_path, channels): - self.material = material - self.data_path = data_path - self.channels = channels - - -class MSFSMaterialAnimation: - bl_options = {"UNDO"} - - extension_name = "ASOBO_property_animation" - - def __new__(cls, *args, **kwargs): - raise RuntimeError("%s should not be instantiated" % cls) - - @staticmethod - def create(import_settings): - # NLA tracks are added bottom to top, so create animations in reverse so the first winds up on top - for gltf2_animation in reversed(import_settings.data.animations): - import_settings.action_cache = {} - import_settings.needs_stash = [] - - if gltf2_animation.extensions is None: - return - - extension = gltf2_animation.extensions.get( - MSFSMaterialAnimation.extension_name - ) - if extension is None: - return - - for channel in extension.get("channels"): - sampler = channel.get("sampler") - target = channel.get("target") - - material_idx = int( - target.split("/")[1] - ) # The target will look something like "materials/INDEX/PROPERTY", so we use this to get the material index - path = target.replace( - f"materials/{material_idx}/", "" - ) # Get the actual path from the target - we have to remove the "materials/INDEX/" prefix from the path to get the path - - action = MSFSMaterialAnimation.get_or_create_action( - import_settings, material_idx, gltf2_animation.track_name - ) - - keys = BinaryData.get_data_from_accessor( - import_settings, gltf2_animation.samplers[sampler].input - ) - values = BinaryData.get_data_from_accessor( - import_settings, gltf2_animation.samplers[sampler].output - ) - - if gltf2_animation.samplers[sampler].interpolation == "CUBICSPLINE": - values = values[1::3] - - blender_path = None - group_name = None - num_components = 0 - if path == "pbrMetallicRoughness/baseColorFactor": - blender_path = "msfs_base_color_factor" - group_name = "Base Color" - num_components = 4 - elif path == "emissiveFactor": - blender_path = "msfs_emissive_factor" - group_name = "Emissive Color" - num_components = 3 - elif path == "pbrMetallicRoughness/metallicFactor": - blender_path = "msfs_metallic_factor" - group_name = "Metallic Factor" - num_components = 1 - elif path == "pbrMetallicRoughness/roughnessFactor": - blender_path = "msfs_roughness_factor" - group_name = "Roughness Factor" - num_components = 1 - elif path == "extensions/ASOBO_material_UV_options/UVOffsetU": - blender_path = "msfs_uv_offset_u" - group_name = "UV Offset U" - num_components = 1 - elif path == "extensions/ASOBO_material_UV_options/UVOffsetV": - blender_path = "msfs_uv_offset_v" - group_name = "UV Offset V" - num_components = 1 - elif path == "extensions/ASOBO_material_UV_options/UVTilingU": - blender_path = "msfs_uv_tiling_u" - group_name = "UV Tiling U" - num_components = 1 - elif path == "extensions/ASOBO_material_UV_options/UVTilingV": - blender_path = "msfs_uv_tiling_v" - group_name = "UV Tiling V" - num_components = 1 - elif path == "extensions/ASOBO_material_UV_options/UVRotation": - blender_path = "msfs_uv_rotation" - group_name = "UV Rotation" - num_components = 1 - elif path == "extensions/ASOBO_material_windshield_v2/wiper1State": - blender_path = "msfs_wiper_1_state" - group_name = "Wiper 1 State" - num_components = 1 - elif path == "extensions/ASOBO_material_windshield_v2/wiper2State": - blender_path = "msfs_wiper_2_state" - group_name = "Wiper 2 State" - num_components = 1 - elif path == "extensions/ASOBO_material_windshield_v2/wiper3State": - blender_path = "msfs_wiper_3_state" - group_name = "Wiper 3 State" - num_components = 1 - elif path == "extensions/ASOBO_material_windshield_v2/wiper4State": - blender_path = "msfs_wiper_4_state" - group_name = "Wiper 4 State" - num_components = 1 - - # Group values by component size - values = np.array_split(values, len(values) / num_components) - - # Create action - fps = bpy.context.scene.render.fps - - coords = [0] * (2 * len(keys)) - coords[::2] = (key[0] * fps for key in keys) - - for i in range(0, num_components): - coords[1::2] = (vals[i] for vals in values) - make_fcurve( - action, - coords, - data_path=blender_path, - index=i, - group_name=group_name, - interpolation=gltf2_animation.samplers[sampler].interpolation, - ) - - # Push all actions onto NLA tracks - for (mat, action) in import_settings.needs_stash: - simulate_stash(mat, gltf2_animation.track_name, action) - - # Unmute track TODO: not sure why it gets muted - look into this - for track in mat.animation_data.nla_tracks: - if track.name == gltf2_animation.track_name: - track.mute = False - - @staticmethod - def get_or_create_action(gltf, material_idx, anim_name): - """ - IMPORT - Utility function to create or get a blender action on a material. If the action already exists, we return instead of creating - - :param gltf: the glTF import plan - :param material_idx: index of glTF material - :param anim_name: name of animation we are importing - """ - mat = gltf.data.materials[material_idx] - mat_name = list(mat.blender_material.values())[ - 0 - ] # The blender_material dictionary will only have one key-value pair, so we can get the value of the first item, which will be the blender material name - - blender_mat = bpy.data.materials[mat_name] - - action = gltf.action_cache.get(mat_name) - if not action: - name = anim_name + "_" + mat_name - action = bpy.data.actions.new(name) - action.id_root = "MATERIAL" - gltf.needs_stash.append((blender_mat, action)) - gltf.action_cache[mat_name] = action - - return action - - @staticmethod - def get_material_from_action(blender_object, blender_action, export_settings): - """ - EXPORT - Utility function to return a blender material from an action, if the action is a material - - :param blender_object: the blender object that is being animated - :param blender_action: the blender action that is being exported - - :return: a blender material, or None - """ - for material_slot in blender_object.material_slots: - material = material_slot.material - - if material is None: - continue - - if material.animation_data is not None: - if blender_action == material.animation_data.action: - return material - elif ( - export_settings["gltf_nla_strips"] is True - ): # Check if the animation is an NLA strip - for track in material.animation_data.nla_tracks: - non_muted_strips = [ - strip - for strip in track.strips - if strip.action is not None and strip.mute is False - ] - if track.strips is None or len(non_muted_strips) != 1: - continue - for strip in non_muted_strips: - if blender_action == strip.action: - return material - - @staticmethod - def gather_actions( - blender_object, gathered_actions, export_settings - ): - """ - EXPORT - Based off code in the Khronos glTF exporter. This looks through all the materials in the object and checks - if there are any animations on them, and if so, add to the actions list. - - :param blender_object: the blender object that is being animated - :param gathered_actions: list of blender actions already gathered - :return: blender_actions, blender_tracks, action_on_type - """ - blender_actions = [] - blender_tracks = {} - action_on_type = {} - - # First step is to get a list of all material animation actions and NLA tracks (if used) - if not ( - blender_object.type == "MESH" - and blender_object.data is not None - and len(blender_object.material_slots) > 0 - ): - return blender_actions, blender_tracks, action_on_type - - for material_slot in blender_object.material_slots: - material = material_slot.material - - if material is None or material.animation_data is None: - continue - - if material.animation_data.action is not None and material.animation_data.action not in gathered_actions: # If more than one object shares this material, the action will get exported multiple times. We prevent that by checking if we've already gathered it - blender_actions.append(material.animation_data.action) - blender_tracks[material.animation_data.action.name] = None - action_on_type[material.animation_data.action.name] = "MATERIAL" - - # Collect associated strips from NLA tracks - if export_settings["gltf_nla_strips"] is True: - for track in material.animation_data.nla_tracks: - # Multi-strip tracks do not export correctly yet (they need to be baked), - # so skip them for now and only write single-strip tracks. - non_muted_strips = [ - strip - for strip in track.strips - if strip.action is not None and strip.mute is False - ] - if track.strips is None or len(non_muted_strips) != 1: - continue - for strip in non_muted_strips: - if strip.action not in gathered_actions: - blender_actions.append(strip.action) - blender_tracks[ - strip.action.name - ] = ( - track.name - ) # Always set after possible active action -> None will be overwrite - action_on_type[strip.action.name] = "MATERIAL" - - return blender_actions, blender_tracks, action_on_type - - @staticmethod - def replace_channel_target( - gltf2_animation_channel_target, channels, blender_object, export_settings - ): - """ - EXPORT - Replace targets for channels that are material animations. We don't use the target object for material targets, instead we - have a path to the material index and cooresponding property. Unfortunately, we don't have access to the finalized glTF material tree yet, - so we need to temporarily keep a reference to the blender material and the value that is being animated. This is properly finalized later. - - :param gltf2_animation_channel_target: a glTF animation channel target - :param channels: list of channel groups gathered by the Khronos exporter. This has the data_path that we're interested in. - :param blender_object: the blender object that is being animated - :param action_name: the name of the blender action being exported - :return: - """ - for channel in channels: - blender_material = MSFSMaterialAnimation.get_material_from_action( - blender_object, channel.id_data, export_settings - ) - if not blender_material: - continue - - gltf2_animation_channel_target.path = MSFSMaterialAnimationTarget( - blender_material, channel.data_path, channels - ) - - # Temporarily set data path to value in order to force the Khronos exporter to gather keyframes. We undo this later in the export process - channel.data_path = "value" - - @staticmethod - def add_placeholder_channel( - gltf2_animation, blender_action, blender_object, export_settings - ): - """ - EXPORT - If we have a glTF animation with only material animations, we need to create a placeholder scale channel. Because we utilize the `extensions` - attribute of the animation, the channels end up being empty, which is against the glTF spec. By using this fake scale channel, we bypass this issue. - - :param gltf2_animation: a glTF animation - :param blender_action: the blender action that is being exported - :param blender_object: the blender object that is being animated - :param export_settings: dictionary of export settings provided by the Khronos exporter - :return: - """ - if not blender_action.fcurves: - return - - for fcurve in blender_action.fcurves: - material = MSFSMaterialAnimation.get_material_from_action( - blender_object, blender_action, export_settings - ) - - if material is None: - # If we actually find a property besides the material animations, we don't need a temp fcurve - return - - # Create temp action and insert fake keyframes - temp_action = bpy.data.actions.new(name="TempAction") - - fcurve = temp_action.fcurves.new(data_path="scale", index=0) - fcurve.keyframe_points.add(1) - - # Collect temp channel and cleanup - gltf2_animation.channels.extend( - gltf2_blender_gather_animation_channels.gather_animation_channels( - temp_action, blender_object, export_settings - ) - ) - - bpy.data.actions.remove(temp_action) - - @staticmethod - def finalize_animation(gltf2_animation): - """ - EXPORT - After the glTF animation is done being gathered, we can move all material animated channels to the Asobo extension and remove it from `channels`. - - :param gltf2_animation: a glTF animation - :return: - """ - material_animation_channels = [] - for channel in list(gltf2_animation.channels): - if not isinstance(channel.target.path, MSFSMaterialAnimationTarget): - continue - - if type(channel.target.path.material) != bpy.types.Material: - continue - - material_animation_channels.append( - {"sampler": channel.sampler, "target": channel.target.path} - ) - - # Restore proper animation channel paths - for blender_channel in channel.target.path.channels: - blender_channel.data_path = channel.target.path.data_path - - gltf2_animation.channels.remove(channel) - - if material_animation_channels: - gltf2_animation.extensions[ - MSFSMaterialAnimation.extension_name - ] = Extension( - name=MSFSMaterialAnimation.extension_name, - extension={"channels": material_animation_channels}, - required=False, - ) - - @staticmethod - def finalize_target(gltf2_animation, gltf2_plan): - """ - EXPORT - Now that we have the finalized material tree, we can properly set the animation channel targets to the proper index, and replace the temporary - blender material reference. - - :param gltf2_animation: a glTF animation - :param gltf2_plan: the finalized glTF data - :return: - """ - if not gltf2_animation.extensions: - return - - if ( - MSFSMaterialAnimation.extension_name - not in gltf2_animation.extensions.keys() - ): - return - - for channel in list(gltf2_animation.extensions[MSFSMaterialAnimation.extension_name][ - "channels" - ]): - material_index = None - for j, material in enumerate(gltf2_plan.materials): - if material.name == channel["target"].material.name: - material_index = j - break - - if material_index is None: - continue - - target_property = channel["target"].data_path - - if target_property == "msfs_base_color_factor": - channel[ - "target" - ] = f"materials/{material_index}/pbrMetallicRoughness/baseColorFactor" - elif target_property == "msfs_emissive_factor": - channel["target"] = f"materials/{material_index}/emissiveFactor" - elif target_property == "msfs_metallic_factor": - channel[ - "target" - ] = f"materials/{material_index}/pbrMetallicRoughness/metallicFactor" - elif target_property == "msfs_roughness_factor": - channel[ - "target" - ] = f"materials/{material_index}/pbrMetallicRoughness/roughnessFactor" - elif target_property == "msfs_uv_offset_u": - channel[ - "target" - ] = f"materials/{material_index}/extensions/ASOBO_material_UV_options/UVOffsetU" - elif target_property == "msfs_uv_offset_v": - channel[ - "target" - ] = f"materials/{material_index}/extensions/ASOBO_material_UV_options/UVOffsetV" - elif target_property == "msfs_uv_tiling_u": - channel[ - "target" - ] = f"materials/{material_index}/extensions/ASOBO_material_UV_options/UVTilingU" - elif target_property == "msfs_uv_tiling_v": - channel[ - "target" - ] = f"materials/{material_index}/extensions/ASOBO_material_UV_options/UVTilingV" - elif target_property == "msfs_uv_rotation": - channel[ - "target" - ] = f"materials/{material_index}/extensions/ASOBO_material_UV_options/UVRotation" - elif target_property == "msfs_wiper_1_state": - channel[ - "target" - ] = f"materials/{material_index}/extensions/ASOBO_material_windshield_v2/wiper1State" - elif target_property == "msfs_wiper_2_state": - channel[ - "target" - ] = f"materials/{material_index}/extensions/ASOBO_material_windshield_v2/wiper2State" - elif target_property == "msfs_wiper_3_state": - channel[ - "target" - ] = f"materials/{material_index}/extensions/ASOBO_material_windshield_v2/wiper3State" - elif target_property == "msfs_wiper_4_state": - channel[ - "target" - ] = f"materials/{material_index}/extensions/ASOBO_material_windshield_v2/wiper4State" - else: - # If we somehow have a property animated that isn't supported, remove the channel - gltf2_animation.extensions[MSFSMaterialAnimation.extension_name]["channels"].remove(channel) From f609b5170a3e959ce9926e3469765f11ecdb056b Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Tue, 15 Mar 2022 11:03:59 -0400 Subject: [PATCH 08/31] refactor: import and export collision gizmos as nodes (#61) * refactor: import and export collision gizmos as nodes * fix: change and to or * fix: loop node children to find collisions * fix: flip scale if YUP * fix: proper scale for gizmos * fix: prevent error if scale is None --- addons/io_scene_gltf2_msfs/io/msfs_export.py | 27 +- addons/io_scene_gltf2_msfs/io/msfs_gizmo.py | 259 ++++++++++++------- addons/io_scene_gltf2_msfs/io/msfs_import.py | 6 +- 3 files changed, 170 insertions(+), 122 deletions(-) diff --git a/addons/io_scene_gltf2_msfs/io/msfs_export.py b/addons/io_scene_gltf2_msfs/io/msfs_export.py index f922c18..ff6bb26 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_export.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_export.py @@ -25,12 +25,9 @@ from .msfs_material import MSFSMaterial class Export: - - gizmoNodes = None def gather_asset_hook(self, gltf2_asset, export_settings): if self.properties.enabled == True: - self.gizmoNodes = [] if gltf2_asset.extensions is None: gltf2_asset.extensions = {} gltf2_asset.extensions["ASOBO_normal_map_convention"] = self.Extension( @@ -48,8 +45,6 @@ def gather_gltf_extensions_hook(self, gltf2_plan, export_settings): def gather_node_hook(self, gltf2_object, blender_object, export_settings): if self.properties.enabled: - if blender_object.msfs_gizmo_type != "NONE": - self.gizmoNodes.append(gltf2_object) if gltf2_object.extensions is None: gltf2_object.extensions = {} @@ -57,29 +52,9 @@ def gather_node_hook(self, gltf2_object, blender_object, export_settings): if blender_object.type == 'LIGHT': MSFSLight.export(gltf2_object, blender_object) - def gather_mesh_hook(self, gltf2_mesh, blender_mesh, blender_object, vertex_groups, modifiers, skip_filter, material_names, export_settings): - if self.properties.enabled: - # Set gizmo objects extension - MSFSGizmo.export(gltf2_mesh, blender_mesh) - def gather_scene_hook(self, gltf2_scene, blender_scene, export_settings): if self.properties.enabled: - # Recursive function to filter children that are gizmos - def get_children(node): - children = [] - for child in node.children: - if child not in self.gizmoNodes: - child.children = get_children(child) - children.append(child) - return children - - # Construct new node list with filtered children - new_nodes = [] - for node in list(gltf2_scene.nodes.copy()): - node.children = get_children(node) - new_nodes.append(node) - - gltf2_scene.nodes = new_nodes + MSFSGizmo.export(gltf2_scene.nodes, blender_scene, export_settings) def gather_material_hook(self, gltf2_material, blender_material, export_settings): if self.properties.enabled: diff --git a/addons/io_scene_gltf2_msfs/io/msfs_gizmo.py b/addons/io_scene_gltf2_msfs/io/msfs_gizmo.py index 9ee4dcf..60c0749 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_gizmo.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_gizmo.py @@ -17,110 +17,179 @@ import bpy import math +from io_scene_gltf2.io.com.gltf2_io import Node from io_scene_gltf2.io.com.gltf2_io_extensions import Extension -class MSFSGizmo(): +class MSFSGizmo: bl_options = {"UNDO"} extension_name = "ASOBO_gizmo_object" def __new__(cls, *args, **kwargs): - raise RuntimeError("%s should not be instantiated" % cls) + raise RuntimeError("%s should not be instantiated" % cls) @staticmethod - def create(gltf2_node, blender_object, import_settings): - gltf_mesh = import_settings.data.meshes[gltf2_node.mesh] - if gltf_mesh.extensions: - extension = gltf_mesh.extensions.get(MSFSGizmo.extension_name) - if extension: - for gizmo_object in extension.get("gizmo_objects"): - bpy.ops.object.empty_add() - gizmo = bpy.context.object - - # Set gizmo location - gizmo.location = gizmo_object.get("translation") - - # Set gizmo type and rename gizmo - type = gizmo_object.get("type") - gizmo.msfs_gizmo_type = type - - if type == "sphere": - gizmo.name = "Sphere Collision" - elif type == "box": - gizmo.name = "Box Collision" - elif type == "cylinder": - gizmo.name = "Cylinder Collision" - - # Get gizmo scale - params = gizmo_object.get("params", {}) - if type == "sphere": - gizmo.scale[0] = params.get("radius") - gizmo.scale[1] = params.get("radius") - gizmo.scale[2] = params.get("radius") - elif type == "cylinder": - gizmo.scale[0] = params.get("radius") - gizmo.scale[1] = params.get("radius") - gizmo.scale[2] = params.get("height") - - # Set road collider - if "Road" in gizmo_object.get("extensions", {}).get("ASOBO_tags", {}).get("tags"): - gizmo.msfs_collision_is_road_collider = True - - gizmo.parent = blender_object - - # Set collection - for collection in gizmo.users_collection: - collection.objects.unlink(gizmo) - blender_object.users_collection[0].objects.link(gizmo) + def create(gltf_scene, blender_scene, import_settings): + """ + Create a "fake" node in the glTF scene to let the gizmo TRS get applied properly relative to the parent + """ + nodes = gltf_scene.nodes + for node_idx in nodes: + node = import_settings.data.nodes[node_idx] + # Check extensions in mesh + if node.mesh is None: + continue + mesh = import_settings.data.meshes[node.mesh] + extension = mesh.extensions.get(MSFSGizmo.extension_name) + if extension is None: + continue + + for gizmo_object in extension.get("gizmo_objects"): + gizmo_type = gizmo_object.get("type") + params = gizmo_object.get("params", {}) + + scale = [1.0, 1.0, 1.0] + if gizmo_type == "sphere": + scale[0] = params.get("radius") + scale[1] = params.get("radius") + scale[2] = params.get("radius") + elif gizmo_type == "box": + scale[0] = params.get("length") / 2 + scale[1] = params.get("width") / 2 + scale[2] = params.get("height") / 2 + elif gizmo_type == "cylinder": + scale[0] = params.get("radius") + scale[1] = params.get("radius") + scale[2] = params.get("height") + + # Flip scale to convert from MSFS gizmo scale system + scale = [scale[1], scale[2], scale[0]] + + placeholder_extension = { + "gizmo_blender_data": { + "road_collider": "Road" + in gizmo_object.get("extensions", {}) + .get("ASOBO_tags", {}) + .get("tags", {}), + "gizmo_type": gizmo_type, + } + } + + # Create new placeholder node + placeholder_node = Node( + camera=None, + children=None, + extensions=placeholder_extension, + extras=None, + matrix=None, + mesh=None, + name="Gizmo", + rotation=gizmo_object.get("rotation"), + scale=scale, + skin=None, + translation=gizmo_object.get("translation"), + weights=None, + ) + + import_settings.data.nodes.append(placeholder_node) + if node.children is None: + node.children = [] + node.children.append(len(import_settings.data.nodes) - 1) @staticmethod - def export(gltf2_mesh, blender_mesh): - gizmo_objects = [] - for object in bpy.context.scene.objects: - if object.type == "MESH" and bpy.data.meshes[object.data.name] == blender_mesh: - for child in object.children: - if child.type == 'EMPTY' and child.msfs_gizmo_type != "NONE": - gizmo_object = {} - gizmo_object["translation"] = list(child.location) - gizmo_object["type"] = child.msfs_gizmo_type - - if child.msfs_gizmo_type == "sphere": - gizmo_object["params"] = { - "radius": abs(child.scale.x * child.scale.y * child.scale.z) - } - elif child.msfs_gizmo_type == "box": - gizmo_object["params"] = { - "length": abs(child.scale.x), - "width": abs(child.scale.y), - "height": abs(child.scale.z) - } - elif child.msfs_gizmo_type == "cylinder": - gizmo_object["params"] = { - "radius": abs(child.scale.x * child.scale.y), - "height": abs(child.scale.z) - } - - tags = ["Collision"] - if child.msfs_collision_is_road_collider: - tags.append("Road") - - gizmo_object["extensions"] = { - "ASOBO_tags": Extension( - name = "ASOBO_tags", - extension = { - "tags": tags - }, - required = False - ) - } - gizmo_objects.append(gizmo_object) - - if gizmo_objects: - gltf2_mesh.extensions[MSFSGizmo.extension_name] = Extension( - name = MSFSGizmo.extension_name, - extension = { - "gizmo_objects": gizmo_objects - }, - required = False - ) + def set_blender_data(gltf2_node, blender_object, import_settings): + """ + Set proper gizmo properties on the blender object + """ + if gltf2_node.extensions is None: + return + + extension = gltf2_node.extensions.get("gizmo_blender_data") + if extension is None: + return + + blender_object.msfs_collision_is_road_collider = extension.get("road_collider") + blender_object.msfs_gizmo_type = extension.get("gizmo_type") + + # Set name + if blender_object.msfs_gizmo_type == "sphere": + blender_object.name = "Sphere Collision" + elif blender_object.msfs_gizmo_type == "box": + blender_object.name = "Box Collision" + elif blender_object.msfs_gizmo_type == "cylinder": + blender_object.name = "Cylinder Collision" + + # The Khronos importer auto-calculates the empty display size, so we need to reset it to 1 + blender_object.empty_display_size = 1.0 + + @staticmethod + def export(nodes, blender_scene, export_settings): + """ + Let the Khronos exporter gather the gizmo to calculate the proper TRS with the parent to make sure everything is correct, + then remove the gizmo from the collected nodes and set the proper mesh extensions + """ + for node in nodes: + collisions = [] # TODO: make sure node is mesh? + for child in node.children: + blender_object = blender_scene.objects.get( + child.name + ) # The glTF exporter will ALWAYS set the node name as the blender name + + if ( + blender_object.parent is None or blender_object.parent.type != "MESH" + ): # We only need the collision gizmos that are parented to a mesh + continue + + if blender_object.msfs_gizmo_type != "NONE": + result = {} + result["type"] = blender_object.msfs_gizmo_type + result["translation"] = child.translation + if child.rotation: + result["rotation"] = child.rotation + + if child.scale is None: # If the scale is default, it will be exported as None which will raise an error here + child.scale = [1.0, 1.0, 1.0] + + # Flip scale to match MSFS gizmo scale system + if export_settings["gltf_yup"]: + child.scale = [child.scale[2], child.scale[0], child.scale[1]] + else: + child.scale = [child.scale[1], child.scale[0], child.scale[2]] + + # Calculate scale per gizmo type + scale = {} + if blender_object.msfs_gizmo_type == "sphere": + scale["radius"] = abs(child.scale[0] * child.scale[1] * child.scale[2]) + elif blender_object.msfs_gizmo_type == "box": + scale["length"] = abs(child.scale[0]) * 2 + scale["width"] = abs(child.scale[1]) * 2 + scale["height"] = abs(child.scale[2]) * 2 + elif blender_object.msfs_gizmo_type == "cylinder": + scale["radius"] = abs(child.scale[0] * child.scale[1]) + scale["height"] = abs(child.scale[2]) + + result["params"] = scale + + # Collision type + tags = ["Collision"] + if blender_object.msfs_collision_is_road_collider: + tags.append("Road") + + result["extensions"] = { + "ASOBO_tags": Extension( + name="ASOBO_tags", extension={"tags": tags}, required=False + ) + } + + collisions.append(result) + node.children.remove(child) + + if collisions: + node.mesh.extensions[MSFSGizmo.extension_name] = Extension( + name=MSFSGizmo.extension_name, + extension={"gizmo_objects": collisions}, + required=False, + ) + + MSFSGizmo.export(node.children, blender_scene, export_settings) diff --git a/addons/io_scene_gltf2_msfs/io/msfs_import.py b/addons/io_scene_gltf2_msfs/io/msfs_import.py index 0eee229..736a9c7 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_import.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_import.py @@ -29,8 +29,12 @@ def gather_import_light_after_hook(self, gltf2_node, blender_node, blender_light MSFSLight.create(gltf2_node, blender_node, blender_light, import_settings) # Create gizmos + def gather_import_scene_before_hook(self, gltf_scene, blender_scene, import_settings): + MSFSGizmo.create(gltf_scene, blender_scene, import_settings) + + # Set proper gizmo blender object properties def gather_import_node_after_hook(self, vnode, gltf2_node, blender_object, import_settings): - MSFSGizmo.create(gltf2_node, blender_object, import_settings) + MSFSGizmo.set_blender_data(gltf2_node, blender_object, import_settings) # Create materials def gather_import_material_after_hook(self, gltf2_material, vertex_color, blender_material, import_settings): From 564bb1d280c60e2d2a4b8118d1c35d28d309a370 Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Wed, 16 Mar 2022 04:28:42 -0400 Subject: [PATCH 09/31] fix: prevent error on some material imports (#66) * fix: prevent error if motion blur extension is none * fix: remove tuple --- addons/io_scene_gltf2_msfs/com/msfs_material_props.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/io_scene_gltf2_msfs/com/msfs_material_props.py b/addons/io_scene_gltf2_msfs/com/msfs_material_props.py index d4c404b..5e5bfed 100644 --- a/addons/io_scene_gltf2_msfs/com/msfs_material_props.py +++ b/addons/io_scene_gltf2_msfs/com/msfs_material_props.py @@ -549,7 +549,7 @@ def from_dict(blender_material, gltf2_material, import_settings): return assert isinstance(extensions, dict) - extension = extensions.get(AsoboDisableMotionBlur.SerializedName) + extension = extensions.get(AsoboDisableMotionBlur.SerializedName, {}) if extension.get("enabled"): blender_material.msfs_disable_motion_blur = True @@ -1678,7 +1678,7 @@ class AsoboMaterialCode: class MaterialCode: Windshield = "Windshield" Porthole = "Porthole" - GeoDecalFrosted = ("GeoDecalFrosted",) + GeoDecalFrosted = "GeoDecalFrosted" ClearCoat = "ClearCoat" @staticmethod From f1c717a84d160d7407bded9481e01bbc928322f4 Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Wed, 16 Mar 2022 04:28:52 -0400 Subject: [PATCH 10/31] fix: wrong material name ref (#67) --- addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py index 3032e81..b189942 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py @@ -107,7 +107,7 @@ def update_msfs_material_type(self, context): msfs_mat = MSFS_Invisible(self, buildTree=True) elif self.msfs_material_type == "msfs_fake_terrain": msfs_mat = MSFS_Fake_Terrain(self, buildTree=True) - elif self.msfs_material_type == "msfs_fresnel": + elif self.msfs_material_type == "msfs_fresnel_fade": msfs_mat = MSFS_Fresnel_Fade(self, buildTree=True) elif self.msfs_material_type == "msfs_env_occluder": msfs_mat = MSFS_Environment_Occluder(self, buildTree=True) From a44193971f66e624d01dbcdad70a3a7486e05963 Mon Sep 17 00:00:00 2001 From: lpierabella <77288191+lpierabella@users.noreply.github.com> Date: Thu, 17 Mar 2022 09:34:25 +0100 Subject: [PATCH 11/31] fix anisotropic unlink issue, fix emissive at 1 when no texture is assigned (#69) --- .../material/msfs_material_anisotropic.py | 6 ++- .../blender/material/msfs_material_hair.py | 2 + .../blender/msfs_material_function.py | 49 ++++++++++++++++++- .../blender/msfs_material_panel.py | 12 ++--- .../blender/msfs_material_prop_update.py | 12 ++--- .../com/msfs_material_props.py | 19 +++---- 6 files changed, 74 insertions(+), 26 deletions(-) diff --git a/addons/io_scene_gltf2_msfs/blender/material/msfs_material_anisotropic.py b/addons/io_scene_gltf2_msfs/blender/material/msfs_material_anisotropic.py index c389d66..14c188a 100644 --- a/addons/io_scene_gltf2_msfs/blender/material/msfs_material_anisotropic.py +++ b/addons/io_scene_gltf2_msfs/blender/material/msfs_material_anisotropic.py @@ -14,7 +14,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from ..msfs_material_function import MSFS_Material +from enum import Enum +from ..msfs_material_function import MSFS_Material, MSFS_ShaderNodes, MSFS_AnisotropicNodes class MSFS_Anisotropic(MSFS_Material): @@ -23,3 +24,6 @@ def __init__(self, material, buildTree=False): def customShaderTree(self): super(MSFS_Anisotropic, self).defaultShaderStree() + super(MSFS_Anisotropic, self).anisotropicShaderTree() + + \ No newline at end of file diff --git a/addons/io_scene_gltf2_msfs/blender/material/msfs_material_hair.py b/addons/io_scene_gltf2_msfs/blender/material/msfs_material_hair.py index 3c87254..26e5796 100644 --- a/addons/io_scene_gltf2_msfs/blender/material/msfs_material_hair.py +++ b/addons/io_scene_gltf2_msfs/blender/material/msfs_material_hair.py @@ -23,3 +23,5 @@ def __init__(self, material, buildTree=False): def customShaderTree(self): super(MSFS_Hair, self).defaultShaderStree() + super(MSFS_Hair, self).anisotropicShaderTree() + diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py index 9eb5685..d65c0ba 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py @@ -107,6 +107,10 @@ class MSFS_ShaderNodes(Enum): blendCompMap = "Blend Occlusion(R) Roughness(G) Metallic(B) Map" vertexColor = "Vertex Color" +class MSFS_AnisotropicNodes(Enum): + anisotropicTex = "Anisotropic Texture" + separateAnisotropic = "Separate Anisotropic" + class MSFS_Material: @@ -156,7 +160,7 @@ def force_update_properties(self): MSFS_Material_Property_Update.update_blend_mask_texture( self.material, bpy.context ) - MSFS_Material_Property_Update.update_wetness_ao_texture( + MSFS_Material_Property_Update.update_extra_slot1_texture( self.material, bpy.context ) MSFS_Material_Property_Update.update_dirt_texture(self.material, bpy.context) @@ -385,7 +389,12 @@ def defaultShaderStree(self): # emissive operators mulEmissiveNode = self.addNode( "ShaderNodeMixRGB", - {"name": MSFS_ShaderNodes.emissiveMul.value, "location": (0.0, -550.0)}, + { + "name": MSFS_ShaderNodes.emissiveMul.value, + "operation": "MULTIPLY", + "location": (0.0, -550.0), + }, + ) # comp operators @@ -559,6 +568,42 @@ def defaultShaderStree(self): 'nodes["{0}"].inputs[20]'.format(MSFS_ShaderNodes.principledBSDF.value), ) + def anisotropicShaderTree(self): + self.nodeAnisotropicTex = self.addNode( + "ShaderNodeTexImage", + {"name": MSFS_AnisotropicNodes.anisotropicTex.value, "location": (-500, -800.0)}, + ) + self.nodeSeparateAnisotropic = self.addNode( + "ShaderNodeSeparateRGB", + {"name": MSFS_AnisotropicNodes.separateAnisotropic.value, "location": (-300, -800.0)}, + ) + self.innerLink( + 'nodes["{0}"].outputs[0]'.format(MSFS_AnisotropicNodes.anisotropicTex.value), + 'nodes["{0}"].inputs[0]'.format(MSFS_AnisotropicNodes.separateAnisotropic.value), + ) + + + def setAnisotropicTex(self, tex): + self.nodeAnisotropicTex = self.getNode(MSFS_AnisotropicNodes.anisotropicTex.value) + self.nodeAnisotropicTex.image = tex + if not self.nodeAnisotropicTex.image: + self.principledBSDF = self.getNode(MSFS_ShaderNodes.principledBSDF.value) + self.unLinkNodeInput(self.principledBSDF, 10) + self.unLinkNodeInput(self.principledBSDF, 11) + elif self.nodeAnisotropicTex.image : + self.innerLink( + 'nodes["{0}"].outputs[0]'.format( + MSFS_AnisotropicNodes.separateAnisotropic.value + ), + 'nodes["{0}"].inputs[10]'.format(MSFS_ShaderNodes.principledBSDF.value), + ) + self.innerLink( + 'nodes["{0}"].outputs[2]'.format( + MSFS_AnisotropicNodes.separateAnisotropic.value + ), + 'nodes["{0}"].inputs[11]'.format(MSFS_ShaderNodes.principledBSDF.value), + ) + def setBaseColor(self, color): self.nodeBaseColorRGB = self.getNode(MSFS_ShaderNodes.baseColorRGB.value) self.nodeBaseColorA = self.getNode(MSFS_ShaderNodes.baseColorA.value) diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py index 7bcdb1d..8340cdc 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py @@ -57,7 +57,7 @@ class MSFS_OT_MigrateMaterialData(bpy.types.Operator): # TODO: Remove eventually "msfs_metallic_texture": "msfs_occlusion_metallic_roughness_texture", "msfs_detail_albedo_texture": "msfs_detail_color_texture", "msfs_detail_metallic_texture": "msfs_detail_occlusion_metallic_roughness_texture", - "msfs_anisotropic_direction_texture": "msfs_wetness_ao_texture", + "msfs_anisotropic_direction_texture": "msfs_extra_slot1_texture", "msfs_clearcoat_texture": "msfs_dirt_texture", "msfs_behind_glass_texture": "msfs_detail_color_texture", } @@ -439,7 +439,7 @@ def draw(self, context): normal_texture_name = "Normal" blend_mask_texture_name = "Blend Mask" dirt_texture_name = "Clearcoat amount (R), Clearcoat rough (G)" - wetness_ao_texture_name = "Wetness AO" + extra_slot1_texture = "Extra Slot 1" emissive_texture_name = "Emissive" detail_color_texture_name = "Secondary Color (RGB), Alpha (A)" detail_occlusion_metallic_roughness_texture_name = ( @@ -456,7 +456,7 @@ def draw(self, context): if mat.msfs_material_type == "msfs_windshield": emissive_texture_name = "Secondary Details(A)" - wetness_ao_texture_name = "Wiper Mask (RG)" + extra_slot1_texture = "Wiper Mask (RG)" detail_color_texture_name = ( "Details Scratch(R), Icing Mask(G), Fingerprints(B)" ) @@ -475,7 +475,7 @@ def draw(self, context): "Emissive Ins Window (RGB), offset Time (A)" ) elif mat.msfs_material_type in ["msfs_anisotropic", "msfs_hair"]: - wetness_ao_texture_name = "Anisotropic direction (RG)" + extra_slot1_texture = "Anisotropic direction (RG)" self.draw_texture_prop( box, mat, "msfs_base_color_texture", text=base_color_tex_name @@ -544,8 +544,8 @@ def draw(self, context): self.draw_texture_prop( box, mat, - "msfs_wetness_ao_texture", - text=wetness_ao_texture_name, + "msfs_extra_slot1_texture", + text=extra_slot1_texture, ) if mat.msfs_material_type == "msfs_clearcoat": self.draw_texture_prop( diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py index b189942..2a3d629 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py @@ -210,14 +210,10 @@ def update_blend_mask_texture(self, context): ) @staticmethod - def update_wetness_ao_texture(self, context): - if self.node_tree.nodes.get("anisotropic_direction", None) != None: - self.node_tree.nodes[ - "anisotropic_direction" - ].image = self.msfs_wetness_ao_texture - self.node_tree.nodes[ - "anisotropic_direction" - ].image.colorspace_settings.name = "Non-Color" + def update_extra_slot1_texture(self, context): + msfs = MSFS_Material_Property_Update.getMaterial(self) + if type(msfs) is MSFS_Anisotropic or type(msfs) is MSFS_Hair: + msfs.setAnisotropicTex(self.msfs_extra_slot1_texture) @staticmethod def update_dirt_texture(self, context): diff --git a/addons/io_scene_gltf2_msfs/com/msfs_material_props.py b/addons/io_scene_gltf2_msfs/com/msfs_material_props.py index 5e5bfed..747f399 100644 --- a/addons/io_scene_gltf2_msfs/com/msfs_material_props.py +++ b/addons/io_scene_gltf2_msfs/com/msfs_material_props.py @@ -189,10 +189,11 @@ class Defaults: type=bpy.types.Image, update=MSFS_Material_Property_Update.update_dirt_texture, ) - bpy.types.Material.msfs_wetness_ao_texture = bpy.props.PointerProperty( - name="Wetness AO Texture", + + bpy.types.Material.msfs_extra_slot1_texture = bpy.props.PointerProperty( + name="Extra Slot 1 Texture", type=bpy.types.Image, - update=MSFS_Material_Property_Update.update_wetness_ao_texture, + update=MSFS_Material_Property_Update.update_extra_slot1_texture, ) bpy.types.Material.msfs_opacity_texture = bpy.props.PointerProperty( name="Opacity Texture", type=bpy.types.Image @@ -1296,7 +1297,7 @@ def from_dict(blender_material, gltf2_material, import_settings): else: blender_material.msfs_material_type = "msfs_anisotropic" if extension.get("anisotropicTexture"): - blender_material.msfs_wetness_ao_texture = MSFSMaterial.create_image( + blender_material.msfs_extra_slot1_texture = MSFSMaterial.create_image( extension.get("anisotropicTexture", {}).get("index"), import_settings ) @@ -1308,10 +1309,10 @@ def to_extension(blender_material, gltf2_material, export_settings): if ( blender_material.msfs_material_type == "msfs_anisotropic" or blender_material.msfs_material_type == "msfs_hair" - ) and blender_material.msfs_wetness_ao_texture is not None: + ) and blender_material.msfs_extra_slot1_texture is not None: result["anisotropicTexture"] = MSFSMaterial.export_image( blender_material, - blender_material.msfs_wetness_ao_texture, + blender_material.msfs_extra_slot1_texture, "DEFAULT", export_settings, ) @@ -1391,7 +1392,7 @@ def from_dict(blender_material, gltf2_material, import_settings): if extension.get("wiper4State"): blender_material.msfs_wiper_4_state = extension.get("wiper4State") if extension.get("wiperMaskTexture"): - blender_material.msfs_wetness_ao_texture = MSFSMaterial.create_image( + blender_material.msfs_extra_slot1_texture = MSFSMaterial.create_image( extension.get("wiperMaskTexture", {}).get("index"), import_settings ) @@ -1406,10 +1407,10 @@ def to_extension(blender_material, gltf2_material, export_settings): result["wiper2State"] = blender_material.msfs_wiper_2_state result["wiper3State"] = blender_material.msfs_wiper_3_state result["wiper4State"] = blender_material.msfs_wiper_4_state - if blender_material.msfs_wetness_ao_texture is not None: + if blender_material.msfs_extra_slot1_texture is not None: result["wiperMaskTexture"] = MSFSMaterial.export_image( blender_material, - blender_material.msfs_wetness_ao_texture, + blender_material.msfs_extra_slot1_texture, "DEFAULT", export_settings, ) From 818443184cde50e169b3ff9d76a4d0745d1fef5c Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Thu, 17 Mar 2022 04:35:09 -0400 Subject: [PATCH 12/31] fix: albedo migration issue (#72) * fix: albedo migration issue * fix: convert to list --- .../io_scene_gltf2_msfs/blender/msfs_material_panel.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py index 8340cdc..8ccff0e 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_panel.py @@ -26,7 +26,6 @@ class MSFS_OT_MigrateMaterialData(bpy.types.Operator): # TODO: Remove eventually bl_label = "Migrate Material Data" old_property_to_new_mapping = { - "msfs_color_albedo_mix": "msfs_base_color_factor", "msfs_color_sss": "msfs_sss_color", "msfs_use_pearl_effect ": "msfs_use_pearl", "msfs_decal_blend_factor_color": "msfs_base_color_blend_factor", @@ -81,7 +80,14 @@ def execute(self, context): del mat[old_property] - # Emissive factor is a special case - old material system had 4 floats, we only need 3 + # Base color is a special case - can only have 3 values, we need 4 + if mat.get("msfs_color_albedo_mix"): + base_color = list(mat.get("msfs_color_albedo_mix")) + if len(base_color) == 3: + base_color.append(1) # Append full alpha + mat.msfs_base_color_factor = base_color + + # Emissive factor is also a special case - old material system had 4 floats, we only need 3 if mat.get("msfs_color_emissive_mix"): mat.msfs_emissive_factor = mat.get("msfs_color_emissive_mix")[0:3] From 8c299ecbf9c16389fb4a69613b417e44769dbd12 Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Thu, 17 Mar 2022 04:35:30 -0400 Subject: [PATCH 13/31] fix: remove dupe node (#71) --- .../io_scene_gltf2_msfs/blender/msfs_material_function.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py index d65c0ba..ce750fc 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py @@ -283,13 +283,6 @@ def defaultShaderStree(self): "ShaderNodeTexImage", {"name": MSFS_ShaderNodes.detailCompTex.value, "location": (-800, -350.0)}, ) - self.nodeDetailNormal = self.addNode( - "ShaderNodeTexImage", - { - "name": MSFS_ShaderNodes.detailNormalTex.value, - "location": (-500, -1300.0), - }, - ) self.nodeDetailNormalScale = self.addNode( "ShaderNodeValue", { From 7022b921576ed678d9975372401db889d4660714 Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Fri, 18 Mar 2022 03:25:35 -0400 Subject: [PATCH 14/31] fix: change operation to blend_type for emissive (#73) --- addons/io_scene_gltf2_msfs/blender/msfs_material_function.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py index ce750fc..a27e009 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py @@ -384,7 +384,7 @@ def defaultShaderStree(self): "ShaderNodeMixRGB", { "name": MSFS_ShaderNodes.emissiveMul.value, - "operation": "MULTIPLY", + "blend_type": "MULTIPLY", "location": (0.0, -550.0), }, From d69a0b0f52e0ccf780351fd5af955e514fef69b0 Mon Sep 17 00:00:00 2001 From: lpierabella <77288191+lpierabella@users.noreply.github.com> Date: Fri, 18 Mar 2022 08:32:03 +0100 Subject: [PATCH 15/31] update glTF-Blender-IO-MSFSversion --- addons/io_scene_gltf2_msfs/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/io_scene_gltf2_msfs/__init__.py b/addons/io_scene_gltf2_msfs/__init__.py index 90a83b0..2790de9 100644 --- a/addons/io_scene_gltf2_msfs/__init__.py +++ b/addons/io_scene_gltf2_msfs/__init__.py @@ -25,7 +25,7 @@ "author": "Luca Pierabella, Wing42, pepperoni505, ronh991, tml1024, and others", "description": "This toolkit prepares your 3D assets to be used for Microsoft Flight Simulator", "blender": (3, 1, 0), - "version": (1, 1, 1), + "version": (1, 1, 2), "location": "File > Import-Export", "category": "Import-Export", "tracker_url": "https://github.com/AsoboStudio/glTF-Blender-IO-MSFS" From a97bfc571e1b2d911fac7d291130adfb0e861533 Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Mon, 21 Mar 2022 05:27:45 -0400 Subject: [PATCH 16/31] fix: use correct file name for lods in xml (#81) --- addons/io_scene_gltf2_msfs/io/msfs_multi_export.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py b/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py index 51267d5..517865b 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py @@ -92,25 +92,26 @@ def execute(self, context): ) lods = etree.SubElement(root, "LODS") - lod_values = [] + lod_files = {} for lod in lod_group.lods: if not MSFS_LODGroupUtility.lod_is_visible(context, lod): continue if lod.enabled: - lod_values.append(lod.lod_value) - lod_values = sorted(lod_values, reverse=True) + lod_files[lod.file_name] = lod.lod_value - for lod_value in lod_values: + lod_files = sorted(lod_files.items(), reverse=True) + + for file_name, lod_value in lod_files: etree.SubElement( lods, "LOD", minSize=str(lod_value), - ModelFile=os.path.splitext(lod.file_name)[0] + ".gltf", + ModelFile=os.path.splitext(file_name)[0] + ".gltf", ) - if lod_values: + if lod_files: # Format XML dom = xml.dom.minidom.parseString(etree.tostring(root)) xml_string = dom.toprettyxml(encoding="utf-8") From b3f481766b1f08e404a41b891b56f23c7ee357a2 Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Mon, 21 Mar 2022 05:29:50 -0400 Subject: [PATCH 17/31] fix: dont create new gltf settings node tree if already exists (#83) --- .../blender/msfs_material_function.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py index a27e009..2916634 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py @@ -205,12 +205,16 @@ def createNodetree(self): self.nodebsdf = self.addNode( "ShaderNodeBsdfPrincipled", {"location": (500.0, 0.0), "hide": False} ) - gltfSettingsNodeTree = bpy.data.node_groups.new( - "glTF Settings", "ShaderNodeTree" - ) - gltfSettingsNodeTree.nodes.new("NodeGroupInput") - gltfSettingsNodeTree.inputs.new("NodeSocketFloat", "Occlusion") - gltfSettingsNodeTree.inputs[0].default_value = 1.000 + if bpy.data.node_groups.get(MSFS_ShaderNodes.glTFSettings.value): + gltfSettingsNodeTree = bpy.data.node_groups[MSFS_ShaderNodes.glTFSettings.value] + else: + gltfSettingsNodeTree = bpy.data.node_groups.new( + "glTF Settings", "ShaderNodeTree" + ) + gltfSettingsNodeTree.nodes.new("NodeGroupInput") + gltfSettingsNodeTree.inputs.new("NodeSocketFloat", "Occlusion") + gltfSettingsNodeTree.inputs[0].default_value = 1.000 + self.nodeglTFSettings = self.addNode( "ShaderNodeGroup", { From fdf8db77e609537339edbe9a47854898f034a371 Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Mon, 21 Mar 2022 05:30:28 -0400 Subject: [PATCH 18/31] fix: use non color for normals (#84) --- addons/io_scene_gltf2_msfs/blender/msfs_material_function.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py index 2916634..f32926d 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py @@ -747,11 +747,13 @@ def setNormalScale(self, scale): def setDetailNormalTex(self, tex): self.nodeDetailNormalTex = self.getNode(MSFS_ShaderNodes.detailNormalTex.value) self.nodeDetailNormalTex.image = tex + self.nodeDetailNormalTex.image.colorspace_settings.name = "Non-Color" self.updateNormalLinks() def setNormalTex(self, tex): self.nodeNormalTex = self.getNode(MSFS_ShaderNodes.normalTex.value) self.nodeNormalTex.image = tex + self.nodeNormalTex.image.colorspace_settings.name = "Non-Color" self.updateNormalLinks() def updateNormalLinks(self): From 2797154a1d9ef5dc29b046d93144431bef922b12 Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Mon, 21 Mar 2022 05:32:39 -0400 Subject: [PATCH 19/31] fix: fresnel fade material not being referenced properly (#88) --- addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py index 2a3d629..a7d47b9 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_prop_update.py @@ -63,7 +63,7 @@ def getMaterial(mat): return MSFS_Invisible(mat) elif mat.msfs_material_type == "msfs_fake_terrain": return MSFS_Fake_Terrain(mat) - elif mat.msfs_material_type == "msfs_fresnel": + elif mat.msfs_material_type == "msfs_fresnel_fade": return MSFS_Fresnel_Fade(mat) elif mat.msfs_material_type == "msfs_env_occluder": return MSFS_Environment_Occluder(mat) From 27bba58c98b775bf49f40c9b5819d4b97e5fd0b6 Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Mon, 21 Mar 2022 11:08:19 -0400 Subject: [PATCH 20/31] fix: remove KHR_lights_punctual from light (#85) --- addons/io_scene_gltf2_msfs/io/msfs_gizmo.py | 3 +++ addons/io_scene_gltf2_msfs/io/msfs_light.py | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/addons/io_scene_gltf2_msfs/io/msfs_gizmo.py b/addons/io_scene_gltf2_msfs/io/msfs_gizmo.py index 60c0749..ede28d3 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_gizmo.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_gizmo.py @@ -136,6 +136,9 @@ def export(nodes, blender_scene, export_settings): child.name ) # The glTF exporter will ALWAYS set the node name as the blender name + if blender_object is None: # However, there are cases where the exporter creates fake nodes that don't exist in the scene + continue + if ( blender_object.parent is None or blender_object.parent.type != "MESH" ): # We only need the collision gizmos that are parented to a mesh diff --git a/addons/io_scene_gltf2_msfs/io/msfs_light.py b/addons/io_scene_gltf2_msfs/io/msfs_light.py index af810be..8951457 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_light.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_light.py @@ -50,6 +50,11 @@ def create(gltf2_node, blender_node, blender_light, import_settings): @staticmethod def export(gltf2_object, blender_object): + # First, clear all KHR_lights_punctual extensions from children. TODO: remove children? + for child in gltf2_object.children: + if child.extensions and child.extensions.get("KHR_lights_punctual"): + child.extensions.pop("KHR_lights_punctual") + angle = 360.0 if blender_object.data.type == 'SPOT': angle = (180.0 / math.pi) * blender_object.data.spot_size From aafef991496c28ae631dd36e17628346cb84bc11 Mon Sep 17 00:00:00 2001 From: lpierabella <77288191+lpierabella@users.noreply.github.com> Date: Tue, 22 Mar 2022 16:02:43 +0100 Subject: [PATCH 21/31] bump version --- addons/io_scene_gltf2_msfs/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/io_scene_gltf2_msfs/__init__.py b/addons/io_scene_gltf2_msfs/__init__.py index 2790de9..7505313 100644 --- a/addons/io_scene_gltf2_msfs/__init__.py +++ b/addons/io_scene_gltf2_msfs/__init__.py @@ -25,7 +25,7 @@ "author": "Luca Pierabella, Wing42, pepperoni505, ronh991, tml1024, and others", "description": "This toolkit prepares your 3D assets to be used for Microsoft Flight Simulator", "blender": (3, 1, 0), - "version": (1, 1, 2), + "version": (1, 1, 3), "location": "File > Import-Export", "category": "Import-Export", "tracker_url": "https://github.com/AsoboStudio/glTF-Blender-IO-MSFS" From 1a544f2cbca5a3c09c16f1698b6baaa904fcf4a0 Mon Sep 17 00:00:00 2001 From: Mike <46610319+Mikeaat@users.noreply.github.com> Date: Thu, 24 Mar 2022 10:19:10 +0100 Subject: [PATCH 22/31] fix: comp material color-space (#92) --- addons/io_scene_gltf2_msfs/blender/msfs_material_function.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py index f32926d..73120b2 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py @@ -720,11 +720,13 @@ def updateColorLinks(self): def setCompTex(self, tex): self.nodeCompTex = self.getNode(MSFS_ShaderNodes.compTex.value) self.nodeCompTex.image = tex + self.nodeCompTex.image.colorspace_settings.name = "Non-Color" self.updateCompLinks() def setDetailCompTex(self, tex): self.nodeDetailCompTex = self.getNode(MSFS_ShaderNodes.detailCompTex.value) self.nodeDetailCompTex.image = tex + self.nodeCompTex.image.colorspace_settings.name = "Non-Color" self.updateCompLinks() def setRoughnessScale(self, scale): From a30073b9e7033eb9b400d76f2fdbad7a7c6046ac Mon Sep 17 00:00:00 2001 From: lpierabella <77288191+lpierabella@users.noreply.github.com> Date: Thu, 24 Mar 2022 11:20:37 +0100 Subject: [PATCH 23/31] fix nodeDetailCompTex color space --- addons/io_scene_gltf2_msfs/blender/msfs_material_function.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py index 73120b2..82f77ef 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py @@ -726,7 +726,7 @@ def setCompTex(self, tex): def setDetailCompTex(self, tex): self.nodeDetailCompTex = self.getNode(MSFS_ShaderNodes.detailCompTex.value) self.nodeDetailCompTex.image = tex - self.nodeCompTex.image.colorspace_settings.name = "Non-Color" + self.nodeDetailCompTex.image.colorspace_settings.name = "Non-Color" self.updateCompLinks() def setRoughnessScale(self, scale): From cec6e26d2f35c54897b84aed33205da2de322d0b Mon Sep 17 00:00:00 2001 From: Boris T Date: Fri, 25 Mar 2022 09:38:16 +0100 Subject: [PATCH 24/31] fix: sort lod in xml (#96) * fix: sort lod in xml should fix #91 * fix: remove minSize for the last lod --- .../io_scene_gltf2_msfs/io/msfs_multi_export.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py b/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py index 517865b..823d1c5 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py @@ -101,15 +101,16 @@ def execute(self, context): if lod.enabled: lod_files[lod.file_name] = lod.lod_value - lod_files = sorted(lod_files.items(), reverse=True) + lod_files = sorted(lod_files.items()) + last_lod = list(lod_files)[-1] for file_name, lod_value in lod_files: - etree.SubElement( - lods, - "LOD", - minSize=str(lod_value), - ModelFile=os.path.splitext(file_name)[0] + ".gltf", - ) + lod_element = etree.SubElement(lods,"LOD") + + if file_name != last_lod[0]: + lod_element.set('minSize',str(lod_value)) + + lod_element.set('ModelFile',os.path.splitext(file_name)[0] + ".gltf") if lod_files: # Format XML From 663000bd70264e5e4139b147d53309d7ae80c0a8 Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Mon, 28 Mar 2022 05:31:24 -0400 Subject: [PATCH 25/31] fix: multi exporter weird spacing (#101) --- addons/io_scene_gltf2_msfs/io/msfs_multi_export_objects.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/io_scene_gltf2_msfs/io/msfs_multi_export_objects.py b/addons/io_scene_gltf2_msfs/io/msfs_multi_export_objects.py index 1e24613..3e04be1 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_multi_export_objects.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_multi_export_objects.py @@ -218,7 +218,6 @@ def draw(self, context): box.label(text="No LODs found in scene") else: for lod_group in lod_groups: - row = layout.row() if ( len(lod_group.lods) == 1 ): # If we only have one LOD in the group, and it is hidden, then don't render the group @@ -228,6 +227,8 @@ def draw(self, context): continue if len(lod_group.lods) > 0: + row = layout.row() + box = row.box() box.prop( lod_group, From 5a3b1b30d31437a32eb61313fcdc77e064821ef3 Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Mon, 28 Mar 2022 08:27:25 -0400 Subject: [PATCH 26/31] fix: last lod list slice (#99) --- addons/io_scene_gltf2_msfs/io/msfs_multi_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py b/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py index 823d1c5..8c6a188 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py @@ -102,7 +102,7 @@ def execute(self, context): lod_files[lod.file_name] = lod.lod_value lod_files = sorted(lod_files.items()) - last_lod = list(lod_files)[-1] + last_lod = list(lod_files)[-1:] for file_name, lod_value in lod_files: lod_element = etree.SubElement(lods,"LOD") From 633ab77a2bbef3d81375d5011cbbcfa66165de7e Mon Sep 17 00:00:00 2001 From: pepperoni505 Date: Mon, 28 Mar 2022 08:31:47 -0400 Subject: [PATCH 27/31] feat: add option to overwrite guid in xml (#100) --- .../io/msfs_multi_export.py | 41 +++++++++++++------ .../io/msfs_multi_export_objects.py | 6 ++- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py b/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py index 8c6a188..2564779 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_multi_export.py @@ -87,9 +87,29 @@ def execute(self, context): for lod_group in lod_groups: # Generate XML if needed if lod_group.generate_xml: - root = etree.Element( - "ModelInfo", guid="{" + str(uuid.uuid4()) + "}", version="1.1" + xml_path = bpy.path.abspath( + os.path.join( + lod_group.folder_name, + lod_group.group_name + ".xml", + ) ) + + found_guid = None + if os.path.exists(xml_path): + tree = etree.parse(xml_path) + found_guid = tree.getroot().attrib.get("guid") + + if lod_group.overwrite_guid or found_guid is None: + root = etree.Element( + "ModelInfo", + guid="{" + str(uuid.uuid4()) + "}", + version="1.1", + ) + else: + root = etree.Element( + "ModelInfo", guid=found_guid, version="1.1" + ) + lods = etree.SubElement(root, "LODS") lod_files = {} @@ -105,12 +125,14 @@ def execute(self, context): last_lod = list(lod_files)[-1:] for file_name, lod_value in lod_files: - lod_element = etree.SubElement(lods,"LOD") - + lod_element = etree.SubElement(lods, "LOD") + if file_name != last_lod[0]: - lod_element.set('minSize',str(lod_value)) + lod_element.set("minSize", str(lod_value)) - lod_element.set('ModelFile',os.path.splitext(file_name)[0] + ".gltf") + lod_element.set( + "ModelFile", os.path.splitext(file_name)[0] + ".gltf" + ) if lod_files: # Format XML @@ -118,12 +140,7 @@ def execute(self, context): xml_string = dom.toprettyxml(encoding="utf-8") with open( - bpy.path.abspath( - os.path.join( - lod_group.folder_name, - lod_group.group_name + ".xml", - ) - ), + xml_path, "wb", ) as f: f.write(xml_string) diff --git a/addons/io_scene_gltf2_msfs/io/msfs_multi_export_objects.py b/addons/io_scene_gltf2_msfs/io/msfs_multi_export_objects.py index 3e04be1..1c497e1 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_multi_export_objects.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_multi_export_objects.py @@ -37,7 +37,8 @@ class MultiExporterLODGroup(bpy.types.PropertyGroup): expanded: bpy.props.BoolProperty(name="", default=True) lods: bpy.props.CollectionProperty(type=MultiExporterLOD) folder_name: bpy.props.StringProperty(name="", default="", subtype="DIR_PATH") - generate_xml: bpy.props.BoolProperty(name="", default=True) + generate_xml: bpy.props.BoolProperty(name="", default=False) + overwrite_guid: bpy.props.BoolProperty(name="", description="If an XML file already exists in the location to export to, the GUID will be overwritten", default=False) class MSFS_LODGroupUtility: @@ -240,6 +241,9 @@ def draw(self, context): ) if lod_group.expanded: box.prop(lod_group, "generate_xml", text="Generate XML") + if lod_group.generate_xml: + box.prop(lod_group, "overwrite_guid", text="Overwrite GUID") + box.prop(lod_group, "folder_name", text="Folder") col = box.column() From 0abbfeee2c50274ba985b6512bd6dfa473232456 Mon Sep 17 00:00:00 2001 From: Jack Lavigne Date: Wed, 30 Mar 2022 03:42:06 -0400 Subject: [PATCH 28/31] fix: convert texture info to dictionary at export (#104) --- addons/io_scene_gltf2_msfs/io/msfs_material.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/io_scene_gltf2_msfs/io/msfs_material.py b/addons/io_scene_gltf2_msfs/io/msfs_material.py index 018e993..02c5b42 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_material.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_material.py @@ -99,7 +99,7 @@ def export_image( if type == "NORMAL": nodes.remove(normal_node) - return texture_info + return texture_info.to_dict() @staticmethod def create(gltf2_material, blender_material, import_settings): From 73728159873e6e74f98595ef08a3fed4371b569c Mon Sep 17 00:00:00 2001 From: Jack Lavigne Date: Mon, 4 Apr 2022 06:32:03 -0400 Subject: [PATCH 29/31] fix: use first tuple for texture info if is tuple (#106) --- addons/io_scene_gltf2_msfs/io/msfs_material.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/io_scene_gltf2_msfs/io/msfs_material.py b/addons/io_scene_gltf2_msfs/io/msfs_material.py index 02c5b42..2dbee81 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_material.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_material.py @@ -99,7 +99,11 @@ def export_image( if type == "NORMAL": nodes.remove(normal_node) - return texture_info.to_dict() + # Some versions of the Khronos exporter have gather_texture_info return a tuple + if isinstance(texture_info, tuple): + texture_info = texture_info[0] + + return texture_info @staticmethod def create(gltf2_material, blender_material, import_settings): From 0be7e49d7782d14183818e53e710caecc17a561f Mon Sep 17 00:00:00 2001 From: Jack Lavigne Date: Mon, 4 Apr 2022 08:55:26 -0400 Subject: [PATCH 30/31] fix: some materials dont import properly (#102) * fix: some materials dont import properly * fix: don't check enabled * fix: add alternate serialized name for import * fix: also check blender image name for create image * fix: use image names to search for texture importing * fix: ensure extension is not None before checking props * fix: missed line * fix: remove unneeded image naming check * fix: check ASOBO_normal_map_convention to see if importing sim asset --- .../blender/msfs_material_function.py | 28 +- .../com/msfs_material_props.py | 327 +++++++++++------- .../io_scene_gltf2_msfs/io/msfs_material.py | 94 ++--- 3 files changed, 243 insertions(+), 206 deletions(-) diff --git a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py index 82f77ef..cb780c7 100644 --- a/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py +++ b/addons/io_scene_gltf2_msfs/blender/msfs_material_function.py @@ -719,15 +719,17 @@ def updateColorLinks(self): def setCompTex(self, tex): self.nodeCompTex = self.getNode(MSFS_ShaderNodes.compTex.value) - self.nodeCompTex.image = tex - self.nodeCompTex.image.colorspace_settings.name = "Non-Color" - self.updateCompLinks() + if tex is not None: + self.nodeCompTex.image = tex + self.nodeCompTex.image.colorspace_settings.name = "Non-Color" + self.updateCompLinks() def setDetailCompTex(self, tex): self.nodeDetailCompTex = self.getNode(MSFS_ShaderNodes.detailCompTex.value) - self.nodeDetailCompTex.image = tex - self.nodeDetailCompTex.image.colorspace_settings.name = "Non-Color" - self.updateCompLinks() + if tex is not None: + self.nodeDetailCompTex.image = tex + self.nodeDetailCompTex.image.colorspace_settings.name = "Non-Color" + self.updateCompLinks() def setRoughnessScale(self, scale): self.nodeRoughnessScale = self.getNode(MSFS_ShaderNodes.roughnessScale.value) @@ -748,15 +750,17 @@ def setNormalScale(self, scale): def setDetailNormalTex(self, tex): self.nodeDetailNormalTex = self.getNode(MSFS_ShaderNodes.detailNormalTex.value) - self.nodeDetailNormalTex.image = tex - self.nodeDetailNormalTex.image.colorspace_settings.name = "Non-Color" - self.updateNormalLinks() + if tex is not None: + self.nodeDetailNormalTex.image = tex + self.nodeDetailNormalTex.image.colorspace_settings.name = "Non-Color" + self.updateNormalLinks() def setNormalTex(self, tex): self.nodeNormalTex = self.getNode(MSFS_ShaderNodes.normalTex.value) - self.nodeNormalTex.image = tex - self.nodeNormalTex.image.colorspace_settings.name = "Non-Color" - self.updateNormalLinks() + if tex is not None: + self.nodeNormalTex.image = tex + self.nodeNormalTex.image.colorspace_settings.name = "Non-Color" + self.updateNormalLinks() def updateNormalLinks(self): self.nodeNormalTex = self.getNode(MSFS_ShaderNodes.normalTex.value) diff --git a/addons/io_scene_gltf2_msfs/com/msfs_material_props.py b/addons/io_scene_gltf2_msfs/com/msfs_material_props.py index 747f399..47eebe8 100644 --- a/addons/io_scene_gltf2_msfs/com/msfs_material_props.py +++ b/addons/io_scene_gltf2_msfs/com/msfs_material_props.py @@ -233,15 +233,10 @@ def from_dict( return assert isinstance(extensions, dict) - # If any Asobo extensions are present, set blender_material to standard. If the blender_material is another type, it will get changed later. This is the only way to see if it's a flight sim blender_material - for key in extensions.keys(): - if key.upper().startswith("ASOBO_"): - blender_material.msfs_material_type = "msfs_standard" - break + # Every flight sim asset has ASOBO_normal_map_convention, so we check if it's being used to set material. We set blender_material to standard. If the blender_material is another type, it will get changed later. + if "ASOBO_normal_map_convention" in import_settings.data.extensions_used: + blender_material.msfs_material_type = "msfs_standard" - if ( - blender_material.msfs_material_type == "msfs_standard" - ): # Only set properties if we are importing a flight sim blender_material if gltf2_material.pbr_metallic_roughness is not None: if gltf2_material.pbr_metallic_roughness.base_color_factor is not None: blender_material.msfs_base_color_factor = gltf2_material.pbr_metallic_roughness.base_color_factor @@ -341,8 +336,13 @@ def from_dict(blender_material, gltf2_material, import_settings): assert isinstance(extensions, dict) extension = extensions.get( - AsoboMaterialGeometryDecal.SerializedName, {} + AsoboMaterialGeometryDecal.SerializedName ) + if extension is None: + return + + blender_material.msfs_material_type = "msfs_geo_decal" + if extension.get("baseColorBlendFactor"): blender_material.msfs_base_color_blend_factor = extension.get( "baseColorBlendFactor" @@ -365,6 +365,7 @@ def to_extension(blender_material, gltf2_material, export_settings): blender_material.msfs_material_type == "msfs_geo_decal" or blender_material.msfs_material_type == "msfs_geo_decal_frosted" ): + result["enabled"] = True result[ "baseColorBlendFactor" ] = blender_material.msfs_base_color_blend_factor @@ -426,8 +427,11 @@ def from_dict(blender_material, gltf2_material, import_settings): assert isinstance(extensions, dict) extension = extensions.get( - AsoboMaterialGhostEffect.SerializedName, {} + AsoboMaterialGhostEffect.SerializedName ) + if extension is None: + return + if extension.get("bias"): blender_material.msfs_ghost_bias = extension.get("bias") if extension.get("scale"): @@ -476,8 +480,11 @@ def from_dict(blender_material, gltf2_material, import_settings): assert isinstance(extensions, dict) extension = extensions.get( - AsoboMaterialDrawOrder.SerializedName, {} + AsoboMaterialDrawOrder.SerializedName ) + if extension is None: + return + if extension.get("drawOrderOffset"): blender_material.msfs_draw_order_offset = extension.get("drawOrderOffset") @@ -518,8 +525,10 @@ def from_dict(blender_material, gltf2_material, import_settings): assert isinstance(extensions, dict) extension = extensions.get(AsoboDayNightCycle.SerializedName) - if extension is not None: - blender_material.msfs_day_night_cycle = True + if extension is None: + return + + blender_material.msfs_day_night_cycle = True @staticmethod def to_extension(blender_material, gltf2_material, export_settings): @@ -550,9 +559,11 @@ def from_dict(blender_material, gltf2_material, import_settings): return assert isinstance(extensions, dict) - extension = extensions.get(AsoboDisableMotionBlur.SerializedName, {}) - if extension.get("enabled"): - blender_material.msfs_disable_motion_blur = True + extension = extensions.get(AsoboDisableMotionBlur.SerializedName) + if extension is None: + return + + blender_material.msfs_disable_motion_blur = True @staticmethod def to_extension(blender_material, gltf2_material, export_settings): @@ -615,9 +626,12 @@ def from_dict(blender_material, gltf2_material, import_settings): return assert isinstance(extensions, dict) - extension = extensions.get(AsoboPearlescent.SerializedName, {}) - if extension: - blender_material.msfs_use_pearl = True + extension = extensions.get(AsoboPearlescent.SerializedName) + if extension is None: + return + + blender_material.msfs_use_pearl = True + if extension.get("pearlShift"): blender_material.msfs_pearl_shift = extension.get("pearlShift") if extension.get("pearlRange"): @@ -653,10 +667,12 @@ def from_dict(blender_material, gltf2_material, import_settings): assert isinstance(extensions, dict) extension = extensions.get( - AsoboAlphaModeDither.SerializedName, {} + AsoboAlphaModeDither.SerializedName ) - if extension.get("enabled"): - blender_material.msfs_alpha_mode = "DITHER" + if extension is None: + return + + blender_material.msfs_alpha_mode = "DITHER" @staticmethod def to_extension(blender_material, gltf2_material, export_settings): @@ -683,10 +699,12 @@ def from_dict(blender_material, gltf2_material, import_settings): assert isinstance(extensions, dict) extension = extensions.get( - AsoboMaterialInvisible.SerializedName, {} + AsoboMaterialInvisible.SerializedName ) - if extension.get("enabled"): - blender_material.msfs_material_type = "msfs_invisible" + if extension is None: + return + + blender_material.msfs_material_type = "msfs_invisible" @staticmethod def to_extension(blender_material, gltf2_material, export_settings): @@ -715,10 +733,12 @@ def from_dict(blender_material, gltf2_material, import_settings): assert isinstance(extensions, dict) extension = extensions.get( - AsoboMaterialEnvironmentOccluder.SerializedName, {} + AsoboMaterialEnvironmentOccluder.SerializedName ) - if extension.get("enabled"): - blender_material.msfs_material_type = "msfs_environment_occluder" + if extension is None: + return + + blender_material.msfs_material_type = "msfs_environment_occluder" @staticmethod def to_extension(blender_material, gltf2_material, export_settings): @@ -814,8 +834,11 @@ def from_dict(blender_material, gltf2_material, import_settings): assert isinstance(extensions, dict) extension = extensions.get( - AsoboMaterialUVOptions.SerializedName, {} + AsoboMaterialUVOptions.SerializedName ) + if extension is None: + return + if extension.get("AOUseUV2"): blender_material.msfs_ao_use_uv2 = extension.get("AOUseUV2") if extension.get("clampUVX"): @@ -898,8 +921,11 @@ def from_dict(blender_material, gltf2_material, import_settings): assert isinstance(extensions, dict) extension = extensions.get( - AsoboMaterialShadowOptions.SerializedName, {} + AsoboMaterialShadowOptions.SerializedName ) + if extension is None: + return + if extension.get("noCastShadow"): blender_material.msfs_no_cast_shadow = extension.get("noCastShadow") @@ -939,8 +965,11 @@ def from_dict(blender_material, gltf2_material, import_settings): assert isinstance(extensions, dict) extension = extensions.get( - AsoboMaterialResponsiveAAOptions.SerializedName, {} + AsoboMaterialResponsiveAAOptions.SerializedName ) + if extension is None: + return + if extension.get("responsiveAA"): blender_material.msfs_responsive_aa = extension.get("responsiveAA") @@ -1019,8 +1048,11 @@ def from_dict(blender_material, gltf2_material, import_settings): assert isinstance(extensions, dict) extension = extensions.get( - AsoboMaterialDetail.SerializedName, {} + AsoboMaterialDetail.SerializedName ) + if extension is None: + return + if extension.get("UVScale"): blender_material.msfs_detail_uv_scale = extension.get("UVScale") if extension.get("UVOffset"): @@ -1135,10 +1167,12 @@ def from_dict(blender_material, gltf2_material, import_settings): assert isinstance(extensions, dict) extension = extensions.get( - AsoboMaterialFakeTerrain.SerializedName, {} + AsoboMaterialFakeTerrain.SerializedName ) - if extension.get("enabled"): - blender_material.msfs_material_type = "msfs_fake_terrain" + if extension is None: + return + + blender_material.msfs_material_type = "msfs_fake_terrain" @staticmethod def to_extension(blender_material, gltf2_material, export_settings): @@ -1186,16 +1220,19 @@ def from_dict(blender_material, gltf2_material, import_settings): assert isinstance(extensions, dict) extension = extensions.get( - AsoboMaterialFresnelFade.SerializedName, {} + AsoboMaterialFresnelFade.SerializedName ) - if extension: - blender_material.msfs_material_type = "msfs_fresnel_fade" - if extension.get("fresnelFactor"): - blender_material.msfs_fresnel_factor = extension.get("fresnelFactor") - if extension.get("fresnelOpacityOffset"): - blender_material.msfs_fresnel_opacity_offset = extension.get( - "fresnelOpacityOffset" - ) + if extension is None: + return + + blender_material.msfs_material_type = "msfs_fresnel_fade" + + if extension.get("fresnelFactor"): + blender_material.msfs_fresnel_factor = extension.get("fresnelFactor") + if extension.get("fresnelOpacityOffset"): + blender_material.msfs_fresnel_opacity_offset = extension.get( + "fresnelOpacityOffset" + ) @staticmethod def to_extension(blender_material, gltf2_material, export_settings): @@ -1243,15 +1280,18 @@ def from_dict(blender_material, gltf2_material, import_settings): return assert isinstance(extensions, dict) - extension = extensions.get(AsoboSSS.SerializedName, {}) - if extension: - blender_material.msfs_material_type = "msfs_sss" - if extension.get("SSSColor"): - blender_material.msfs_sss_color = extension.get("SSSColor") - if extension.get("opacityTexture"): - blender_material.msfs_opacity_texture = MSFSMaterial.create_image( - extension.get("opacityTexture", {}).get("index"), import_settings - ) + extension = extensions.get(AsoboSSS.SerializedName) + if extension is None: + return + + blender_material.msfs_material_type = "msfs_sss" + + if extension.get("SSSColor"): + blender_material.msfs_sss_color = extension.get("SSSColor") + if extension.get("opacityTexture"): + blender_material.msfs_opacity_texture = MSFSMaterial.create_image( + extension.get("opacityTexture", {}).get("index"), import_settings + ) @staticmethod def to_extension(blender_material, gltf2_material, export_settings): @@ -1289,17 +1329,19 @@ def from_dict(blender_material, gltf2_material, import_settings): return assert isinstance(extensions, dict) - extension = extensions.get(AsoboAnisotropic.SerializedName, {}) - if extension: - # MUST BE CALLED AFTER SSS - if blender_material.msfs_material_type == "msfs_sss": - blender_material.msfs_material_type = "msfs_hair" # SSS and hair share identical properties, except for this. If present, switch from SSS to hair - else: - blender_material.msfs_material_type = "msfs_anisotropic" - if extension.get("anisotropicTexture"): - blender_material.msfs_extra_slot1_texture = MSFSMaterial.create_image( - extension.get("anisotropicTexture", {}).get("index"), import_settings - ) + extension = extensions.get(AsoboAnisotropic.SerializedName) + if extension is None: + return + + # MUST BE CALLED AFTER SSS + if blender_material.msfs_material_type == "msfs_sss": + blender_material.msfs_material_type = "msfs_hair" # SSS and hair share identical properties, except for this. If present, switch from SSS to hair + else: + blender_material.msfs_material_type = "msfs_anisotropic" + if extension.get("anisotropicTexture"): + blender_material.msfs_extra_slot1_texture = MSFSMaterial.create_image( + extension.get("anisotropicTexture", {}).get("index"), import_settings + ) @staticmethod def to_extension(blender_material, gltf2_material, export_settings): @@ -1325,6 +1367,7 @@ def to_extension(blender_material, gltf2_material, export_settings): class AsoboWindshield: SerializedName = "ASOBO_material_windshield_v2" + AlternateSerializedName = "ASOBO_material_windshield" class Defaults: rainDropScale = 1.0 @@ -1378,23 +1421,28 @@ def from_dict(blender_material, gltf2_material, import_settings): return assert isinstance(extensions, dict) - extension = extensions.get(AsoboWindshield.SerializedName, {}) - if extension: - blender_material.msfs_material_type = "msfs_windshield" - if extension.get("rainDropScale"): - blender_material.msfs_rain_drop_scale = extension.get("rainDropScale") - if extension.get("wiper1State"): - blender_material.msfs_wiper_1_state = extension.get("wiper1State") - if extension.get("wiper2State"): - blender_material.msfs_wiper_2_state = extension.get("wiper2State") - if extension.get("wiper3State"): - blender_material.msfs_wiper_3_state = extension.get("wiper3State") - if extension.get("wiper4State"): - blender_material.msfs_wiper_4_state = extension.get("wiper4State") - if extension.get("wiperMaskTexture"): - blender_material.msfs_extra_slot1_texture = MSFSMaterial.create_image( - extension.get("wiperMaskTexture", {}).get("index"), import_settings - ) + extension = extensions.get(AsoboWindshield.SerializedName) + if not extension: + extension = extensions.get(AsoboWindshield.AlternateSerializedName) + if extension is None: + return + + blender_material.msfs_material_type = "msfs_windshield" + + if extension.get("rainDropScale"): + blender_material.msfs_rain_drop_scale = extension.get("rainDropScale") + if extension.get("wiper1State"): + blender_material.msfs_wiper_1_state = extension.get("wiper1State") + if extension.get("wiper2State"): + blender_material.msfs_wiper_2_state = extension.get("wiper2State") + if extension.get("wiper3State"): + blender_material.msfs_wiper_3_state = extension.get("wiper3State") + if extension.get("wiper4State"): + blender_material.msfs_wiper_4_state = extension.get("wiper4State") + if extension.get("wiperMaskTexture"): + blender_material.msfs_extra_slot1_texture = MSFSMaterial.create_image( + extension.get("wiperMaskTexture", {}).get("index"), import_settings + ) @staticmethod def to_extension(blender_material, gltf2_material, export_settings): @@ -1433,13 +1481,16 @@ def from_dict(blender_material, gltf2_material, import_settings): return assert isinstance(extensions, dict) - extension = extensions.get(AsoboClearCoat.SerializedName, {}) - if extension: - blender_material.msfs_material_type = "msfs_clearcoat" - if extension.get("dirtTexture"): - blender_material.msfs_dirt_texture = MSFSMaterial.create_image( - extension.get("dirtTexture", {}).get("index"), import_settings - ) + extension = extensions.get(AsoboClearCoat.SerializedName) + if extension is None: + return + + blender_material.msfs_material_type = "msfs_clearcoat" + + if extension.get("dirtTexture"): + blender_material.msfs_dirt_texture = MSFSMaterial.create_image( + extension.get("dirtTexture", {}).get("index"), import_settings + ) @staticmethod def to_extension(blender_material, gltf2_material, export_settings): @@ -1517,24 +1568,26 @@ def from_dict(blender_material, gltf2_material, import_settings): assert isinstance(extensions, dict) extension = extensions.get( - AsoboParallaxWindow.SerializedName, {} + AsoboParallaxWindow.SerializedName ) - if extension: - blender_material.msfs_material_type = "msfs_parallax" - if extension.get("parallaxScale"): - blender_material.msfs_parallax_scale = extension.get("parallaxScale") - if extension.get("roomSizeXScale"): - blender_material.msfs_parallax_room_size_x = extension.get("roomSizeXScale") - if extension.get("roomSizeYScale"): - blender_material.msfs_parallax_room_size_y = extension.get("roomSizeYScale") - if extension.get("roomNumberXY"): - blender_material.msfs_parallax_room_number_xy = extension.get("roomNumberXY") - if extension.get("corridor"): - blender_material.msfs_parallax_corridor = extension.get("corridor") - if extension.get("behindWindowMapTexture"): - blender_material.msfs_detail_color_texture = MSFSMaterial.create_image( - extension.get("behindWindowMapTexture", {}).get("index"), import_settings - ) + if extension is None: + return + + blender_material.msfs_material_type = "msfs_parallax" + if extension.get("parallaxScale"): + blender_material.msfs_parallax_scale = extension.get("parallaxScale") + if extension.get("roomSizeXScale"): + blender_material.msfs_parallax_room_size_x = extension.get("roomSizeXScale") + if extension.get("roomSizeYScale"): + blender_material.msfs_parallax_room_size_y = extension.get("roomSizeYScale") + if extension.get("roomNumberXY"): + blender_material.msfs_parallax_room_number_xy = extension.get("roomNumberXY") + if extension.get("corridor"): + blender_material.msfs_parallax_corridor = extension.get("corridor") + if extension.get("behindWindowMapTexture"): + blender_material.msfs_detail_color_texture = MSFSMaterial.create_image( + extension.get("behindWindowMapTexture", {}).get("index"), import_settings + ) @staticmethod def to_extension(blender_material, gltf2_material, export_settings): @@ -1566,6 +1619,7 @@ def to_extension(blender_material, gltf2_material, export_settings): class AsoboGlass: SerializedName = "ASOBO_material_glass" + AlternateSerializedName = "ASOBO_material_kitty_glass" class Defaults: glassReflectionMaskFactor = 0.0 @@ -1593,17 +1647,22 @@ def from_dict(blender_material, gltf2_material, import_settings): return assert isinstance(extensions, dict) - extension = extensions.get(AsoboGlass.SerializedName, {}) - if extension: - blender_material.msfs_material_type = "msfs_glass" - if extension.get("glassReflectionMaskFactor"): - blender_material.msfs_glass_reflection_mask_factor = extension.get( - "glassReflectionMaskFactor" - ) - if extension.get("glassDeformationFactor"): - blender_material.msfs_glass_deformation_factor = extension.get( - "glassDeformationFactor" - ) + extension = extensions.get(AsoboGlass.SerializedName) + if not extension: + extension = extensions.get(AsoboGlass.AlternateSerializedName) + if extension is None: + return + + blender_material.msfs_material_type = "msfs_glass" + + if extension.get("glassReflectionMaskFactor"): + blender_material.msfs_glass_reflection_mask_factor = extension.get( + "glassReflectionMaskFactor" + ) + if extension.get("glassDeformationFactor"): + blender_material.msfs_glass_deformation_factor = extension.get( + "glassDeformationFactor" + ) @staticmethod def to_extension(blender_material, gltf2_material, export_settings): @@ -1647,12 +1706,14 @@ def from_dict(blender_material, gltf2_material, import_settings): return assert isinstance(extensions, dict) - extension = extensions.get(AsoboTags.SerializedName, []) - if extension: - if AsoboTags.AsoboTag.Collision in extension.get("tags"): - blender_material.msfs_collision_material = True - if AsoboTags.AsoboTag.Road in extension.get("tags"): - blender_material.msfs_road_collision_material = True + extension = extensions.get(AsoboTags.SerializedName) + if extension is None: + return + + if AsoboTags.AsoboTag.Collision in extension.get("tags"): + blender_material.msfs_collision_material = True + if AsoboTags.AsoboTag.Road in extension.get("tags"): + blender_material.msfs_road_collision_material = True @staticmethod def to_extension(blender_material, gltf2_material, export_settings): @@ -1689,16 +1750,18 @@ def from_dict(blender_material, gltf2_material, import_settings): return assert isinstance(extras, dict) - extension = extras.get(AsoboMaterialCode.SerializedName, []) - if extension: - if AsoboMaterialCode.MaterialCode.Windshield in extension: - blender_material.msfs_material_type = "msfs_windshield" - elif AsoboMaterialCode.MaterialCode.Porthole in extension: - blender_material.msfs_material_type = "msfs_porthole" - elif AsoboMaterialCode.MaterialCode.GeoDecalFrosted in extension: - blender_material.msfs_material_type = "msfs_geo_decal_frosted" - elif AsoboMaterialCode.MaterialCode.ClearCoat in extension: - blender_material.msfs_material_type = "msfs_clearcoat" + extension = extras.get(AsoboMaterialCode.SerializedName) + if extension is None: + return + + if extension == AsoboMaterialCode.MaterialCode.Windshield: + blender_material.msfs_material_type = "msfs_windshield" + elif extension == AsoboMaterialCode.MaterialCode.Porthole: + blender_material.msfs_material_type = "msfs_porthole" + elif extension == AsoboMaterialCode.MaterialCode.GeoDecalFrosted: + blender_material.msfs_material_type = "msfs_geo_decal_frosted" + elif extension == AsoboMaterialCode.MaterialCode.ClearCoat: + blender_material.msfs_material_type = "msfs_clearcoat" @staticmethod def to_extension(blender_material, gltf2_material, export_settings): diff --git a/addons/io_scene_gltf2_msfs/io/msfs_material.py b/addons/io_scene_gltf2_msfs/io/msfs_material.py index 2dbee81..9d31fe2 100644 --- a/addons/io_scene_gltf2_msfs/io/msfs_material.py +++ b/addons/io_scene_gltf2_msfs/io/msfs_material.py @@ -30,6 +30,33 @@ class MSFSMaterial: bl_options = {"UNDO"} + extensions = [ + MSFSMaterialExtensions.AsoboMaterialCommon, + MSFSMaterialExtensions.AsoboMaterialGeometryDecal, + MSFSMaterialExtensions.AsoboMaterialGhostEffect, + MSFSMaterialExtensions.AsoboMaterialDrawOrder, + MSFSMaterialExtensions.AsoboDayNightCycle, + MSFSMaterialExtensions.AsoboDisableMotionBlur, + MSFSMaterialExtensions.AsoboPearlescent, + MSFSMaterialExtensions.AsoboAlphaModeDither, + MSFSMaterialExtensions.AsoboMaterialInvisible, + MSFSMaterialExtensions.AsoboMaterialEnvironmentOccluder, + MSFSMaterialExtensions.AsoboMaterialUVOptions, + MSFSMaterialExtensions.AsoboMaterialShadowOptions, + MSFSMaterialExtensions.AsoboMaterialResponsiveAAOptions, + MSFSMaterialExtensions.AsoboMaterialDetail, + MSFSMaterialExtensions.AsoboMaterialFakeTerrain, + MSFSMaterialExtensions.AsoboMaterialFresnelFade, + MSFSMaterialExtensions.AsoboSSS, + MSFSMaterialExtensions.AsoboAnisotropic, + MSFSMaterialExtensions.AsoboWindshield, + MSFSMaterialExtensions.AsoboClearCoat, + MSFSMaterialExtensions.AsoboParallaxWindow, + MSFSMaterialExtensions.AsoboGlass, + MSFSMaterialExtensions.AsoboTags, + MSFSMaterialExtensions.AsoboMaterialCode, + ] + def __new__(cls, *args, **kwargs): raise RuntimeError("%s should not be instantiated" % cls) @@ -40,12 +67,9 @@ def create_image(index, import_settings): pyimg = import_settings.data.images[pytexture.source] # Find image created - if pyimg.name in bpy.data.images: - return bpy.data.images[pyimg.name] - elif os.path.basename(pyimg.uri) in bpy.data.images: - return bpy.data.images[pyimg.uri] - elif "Image_%d" % index in bpy.data.images: - return bpy.data.images["Image_%d" % index] + blender_image_name = pyimg.blender_image_name + if blender_image_name: + return bpy.data.images[blender_image_name] @staticmethod def export_image( @@ -107,64 +131,10 @@ def export_image( @staticmethod def create(gltf2_material, blender_material, import_settings): - extensions = [ - MSFSMaterialExtensions.AsoboMaterialCommon, - MSFSMaterialExtensions.AsoboMaterialGeometryDecal, - MSFSMaterialExtensions.AsoboMaterialGhostEffect, - MSFSMaterialExtensions.AsoboMaterialDrawOrder, - MSFSMaterialExtensions.AsoboDayNightCycle, - MSFSMaterialExtensions.AsoboDisableMotionBlur, - MSFSMaterialExtensions.AsoboPearlescent, - MSFSMaterialExtensions.AsoboAlphaModeDither, - MSFSMaterialExtensions.AsoboMaterialInvisible, - MSFSMaterialExtensions.AsoboMaterialEnvironmentOccluder, - MSFSMaterialExtensions.AsoboMaterialUVOptions, - MSFSMaterialExtensions.AsoboMaterialShadowOptions, - MSFSMaterialExtensions.AsoboMaterialResponsiveAAOptions, - MSFSMaterialExtensions.AsoboMaterialDetail, - MSFSMaterialExtensions.AsoboMaterialFakeTerrain, - MSFSMaterialExtensions.AsoboMaterialFresnelFade, - MSFSMaterialExtensions.AsoboSSS, - MSFSMaterialExtensions.AsoboAnisotropic, - MSFSMaterialExtensions.AsoboWindshield, - MSFSMaterialExtensions.AsoboClearCoat, - MSFSMaterialExtensions.AsoboParallaxWindow, - MSFSMaterialExtensions.AsoboGlass, - MSFSMaterialExtensions.AsoboTags, - MSFSMaterialExtensions.AsoboMaterialCode, - ] - - for extension in extensions: + for extension in MSFSMaterial.extensions: extension.from_dict(blender_material, gltf2_material, import_settings) @staticmethod def export(gltf2_material, blender_material, export_settings): - extensions = [ - MSFSMaterialExtensions.AsoboMaterialCommon, - MSFSMaterialExtensions.AsoboMaterialGeometryDecal, - MSFSMaterialExtensions.AsoboMaterialGhostEffect, - MSFSMaterialExtensions.AsoboMaterialDrawOrder, - MSFSMaterialExtensions.AsoboDayNightCycle, - MSFSMaterialExtensions.AsoboDisableMotionBlur, - MSFSMaterialExtensions.AsoboPearlescent, - MSFSMaterialExtensions.AsoboAlphaModeDither, - MSFSMaterialExtensions.AsoboMaterialInvisible, - MSFSMaterialExtensions.AsoboMaterialEnvironmentOccluder, - MSFSMaterialExtensions.AsoboMaterialUVOptions, - MSFSMaterialExtensions.AsoboMaterialShadowOptions, - MSFSMaterialExtensions.AsoboMaterialResponsiveAAOptions, - MSFSMaterialExtensions.AsoboMaterialDetail, - MSFSMaterialExtensions.AsoboMaterialFakeTerrain, - MSFSMaterialExtensions.AsoboMaterialFresnelFade, - MSFSMaterialExtensions.AsoboSSS, - MSFSMaterialExtensions.AsoboAnisotropic, - MSFSMaterialExtensions.AsoboWindshield, - MSFSMaterialExtensions.AsoboClearCoat, - MSFSMaterialExtensions.AsoboParallaxWindow, - MSFSMaterialExtensions.AsoboGlass, - MSFSMaterialExtensions.AsoboTags, - MSFSMaterialExtensions.AsoboMaterialCode, - ] - - for extension in extensions: + for extension in MSFSMaterial.extensions: extension.to_extension(blender_material, gltf2_material, export_settings) From 2cadb5a7585ee35ce041bc1b45c01aa5464264a7 Mon Sep 17 00:00:00 2001 From: lpierabella <77288191+lpierabella@users.noreply.github.com> Date: Mon, 4 Apr 2022 14:58:17 +0200 Subject: [PATCH 31/31] updated version --- addons/io_scene_gltf2_msfs/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/io_scene_gltf2_msfs/__init__.py b/addons/io_scene_gltf2_msfs/__init__.py index 7505313..cf4ee1c 100644 --- a/addons/io_scene_gltf2_msfs/__init__.py +++ b/addons/io_scene_gltf2_msfs/__init__.py @@ -25,7 +25,7 @@ "author": "Luca Pierabella, Wing42, pepperoni505, ronh991, tml1024, and others", "description": "This toolkit prepares your 3D assets to be used for Microsoft Flight Simulator", "blender": (3, 1, 0), - "version": (1, 1, 3), + "version": (1, 1, 4), "location": "File > Import-Export", "category": "Import-Export", "tracker_url": "https://github.com/AsoboStudio/glTF-Blender-IO-MSFS"