diff --git a/addons/io_scene_swbf_msh/__init__.py b/addons/io_scene_swbf_msh/__init__.py index 04fcaf6..1bb8331 100644 --- a/addons/io_scene_swbf_msh/__init__.py +++ b/addons/io_scene_swbf_msh/__init__.py @@ -64,6 +64,7 @@ from .msh_skeleton_properties import * from .msh_collision_prim_properties import * from .msh_to_blend import * from .zaa_to_blend import * +from .material_props_to_nodes_op import GenerateMaterialFromSWBFProperties class ExportMSH(Operator, ExportHelper): @@ -282,6 +283,8 @@ def register(): bpy.utils.register_class(VIEW3D_MT_SWBF) bpy.types.VIEW3D_MT_object_context_menu.append(draw_matfill_menu) + bpy.utils.register_class(GenerateMaterialFromSWBFProperties) + def unregister(): @@ -304,6 +307,9 @@ def unregister(): bpy.utils.unregister_class(VIEW3D_MT_SWBF) bpy.types.VIEW3D_MT_object_context_menu.remove(draw_matfill_menu) + bpy.utils.unregister_class(GenerateMaterialFromSWBFProperties) + + if __name__ == "__main__": register() diff --git a/addons/io_scene_swbf_msh/material_props_to_nodes_op.py b/addons/io_scene_swbf_msh/material_props_to_nodes_op.py new file mode 100644 index 0000000..dcea351 --- /dev/null +++ b/addons/io_scene_swbf_msh/material_props_to_nodes_op.py @@ -0,0 +1,71 @@ +""" Operator for creating/modifying nodes to approximate appearance of SWBF material. + Only relevant if the builtin Eevee renderer is being used. """ + +import bpy +from typing import Dict +from .msh_material import * +from .msh_material_gather import * +from .msh_material_properties import * + +from .msh_material_utilities import _REVERSE_RENDERTYPES_MAPPING + +from math import sqrt + + +from bpy.props import BoolProperty, EnumProperty, StringProperty +from bpy.types import Operator, Menu + + + + + + +class GenerateMaterialFromSWBFProperties(bpy.types.Operator): + + bl_idname = "swbf_msh.generate_material" + bl_label = "Generate SWBF Material" + bl_description = "Generate a Blender material from SWBF material properties." + + + material_name: StringProperty(name = "Material Name", description = "Name of material whose SWBF properties nodes will be generated from.") + + + def execute(self, context): + + material = bpy.data.materials[self.material_name] + + + if material and material.swbf_msh_mat: + + mat_props = material.swbf_msh_mat + + material.node_tree.nodes.clear() + + bsdf = material.node_tree.nodes.new("ShaderNodeBsdfPrincipled") + + diffuse_texture_path = mat_props.diffuse_map + if diffuse_texture_path: + texImage = material.node_tree.nodes.new('ShaderNodeTexImage') + texImage.image = bpy.data.images.load(diffuse_texture_path) + texImage.image.alpha_mode = 'CHANNEL_PACKED' + material.node_tree.links.new(bsdf.inputs['Base Color'], texImage.outputs['Color']) + + bsdf.inputs["Roughness"].default_value = 1.0 + bsdf.inputs["Specular"].default_value = 0.0 + + if mat_props.hardedged_transparency: + material.blend_method = "CLIP" + material.node_tree.links.new(bsdf.inputs['Alpha'], texImage.outputs['Alpha']) + + material.use_backface_culling = not bool(mat_props.doublesided) + + + output = material.node_tree.nodes.new("ShaderNodeOutputMaterial") + material.node_tree.links.new(output.inputs['Surface'], bsdf.outputs['BSDF']) + + + return {'FINISHED'} + + + + diff --git a/addons/io_scene_swbf_msh/msh_material_gather.py b/addons/io_scene_swbf_msh/msh_material_gather.py index 39ea6d4..cbbb78b 100644 --- a/addons/io_scene_swbf_msh/msh_material_gather.py +++ b/addons/io_scene_swbf_msh/msh_material_gather.py @@ -7,6 +7,8 @@ from .msh_material import * from .msh_material_utilities import _RENDERTYPES_MAPPING +import os + def gather_materials() -> Dict[str, Material]: """ Gathers the Blender materials and returns them as a dictionary of strings and Material objects. """ @@ -35,16 +37,16 @@ def read_material(blender_material: bpy.types.Material) -> Material: result.data = _read_material_props_data(props) if "UNSUPPORTED" not in props.rendertype: - result.texture0 = props.diffuse_map + result.texture0 = os.path.basename(props.diffuse_map) result.texture1 = _read_normal_map_or_distortion_map_texture(props) result.texture2 = _read_detail_texture(props) result.texture3 = _read_envmap_texture(props) else: - result.texture0 = props.texture_0 - result.texture1 = props.texture_1 - result.texture2 = props.texture_2 - result.texture3 = props.texture_3 + result.texture0 = os.path.basename(props.texture_0) + result.texture1 = os.path.basename(props.texture_1) + result.texture2 = os.path.basename(props.texture_2) + result.texture3 = os.path.basename(props.texture_3) return result @@ -96,11 +98,13 @@ def _read_material_props_data(props) -> Tuple[int, int]: return (props.detail_map_tiling_u, props.detail_map_tiling_v) + + def _read_normal_map_or_distortion_map_texture(props) -> str: if "REFRACTION" in props.rendertype: - return props.distortion_map + return os.path.basename(props.distortion_map) if "NORMALMAPPED" in props.rendertype: - return props.normal_map + return os.path.basename(props.normal_map) return "" @@ -108,10 +112,10 @@ def _read_detail_texture(props) -> str: if "REFRACTION" in props.rendertype: return "" - return props.detail_map + return os.path.basename(props.detail_map) def _read_envmap_texture(props) -> str: if "ENVMAPPED" not in props.rendertype: return "" - return props.environment_map + return os.path.basename(props.environment_map) diff --git a/addons/io_scene_swbf_msh/msh_material_properties.py b/addons/io_scene_swbf_msh/msh_material_properties.py index 820d5db..a817e61 100644 --- a/addons/io_scene_swbf_msh/msh_material_properties.py +++ b/addons/io_scene_swbf_msh/msh_material_properties.py @@ -9,6 +9,10 @@ from .msh_material_ui_strings import * from .msh_material_utilities import _REVERSE_RENDERTYPES_MAPPING +from .material_props_to_nodes_op import GenerateMaterialFromSWBFProperties + + + UI_MATERIAL_RENDERTYPES = ( ('NORMAL_BF2', "00 Standard (SWBF2)", UI_RENDERTYPE_NORMAL_BF2_DESC), ('SCROLLING_BF2', "03 Scrolling (SWBF2)", UI_RENDERTYPE_SCROLLING_BF2_DESC), @@ -167,7 +171,8 @@ class MaterialProperties(PropertyGroup): description="The basic diffuse map for the material. The alpha channel " "is either the Transparency Map, Glow Map or Gloss Map, " "depending on the selected rendertype and flags.", - default="white.tga") + default="white.tga", + subtype='FILE_PATH') detail_map: StringProperty(name="Detail Map", description="Detail maps allow you to add in 'detail' to the diffuse " @@ -285,3 +290,7 @@ class MaterialPropertiesPanel(bpy.types.Panel): layout.prop(material_props, "texture_2") layout.prop(material_props, "texture_3") + + op_props = layout.operator("swbf_msh.generate_material", text="Generate Nodes") + op_props.material_name = context.material.name + diff --git a/addons/io_scene_swbf_msh/msh_material_to_blend.py b/addons/io_scene_swbf_msh/msh_material_to_blend.py index 398abc5..5400953 100644 --- a/addons/io_scene_swbf_msh/msh_material_to_blend.py +++ b/addons/io_scene_swbf_msh/msh_material_to_blend.py @@ -33,8 +33,38 @@ def find_texture_path(folder_path : str, name : str) -> str: return "" +def swbf_material_to_blend(material_name : str, material : Material, folder_path : str) -> bpy.types.Material: -def fill_material_props(material : Material, material_properties): + new_mat = bpy.data.materials.new(name=material_name) + new_mat.use_nodes = True + bsdf = new_mat.node_tree.nodes["Principled BSDF"] + + diffuse_texture_path = find_texture_path(folder_path, material.texture0) + + if diffuse_texture_path: + texImage = new_mat.node_tree.nodes.new('ShaderNodeTexImage') + texImage.image = bpy.data.images.load(diffuse_texture_path) + texImage.image.alpha_mode = 'CHANNEL_PACKED' + new_mat.node_tree.links.new(bsdf.inputs['Base Color'], texImage.outputs['Color']) + + bsdf.inputs["Roughness"].default_value = 1.0 + bsdf.inputs["Specular"].default_value = 0.0 + + if material.flags & MaterialFlags.HARDEDGED_TRANSPARENCY: + new_mat.blend_method = "CLIP" + new_mat.node_tree.links.new(bsdf.inputs['Alpha'], texImage.outputs['Alpha']) + + + new_mat.use_backface_culling = not bool(material.flags & MaterialFlags.DOUBLESIDED) + + + fill_material_props(material, new_mat.swbf_msh_mat, folder_path) + + return new_mat + + + +def fill_material_props(material : Material, material_properties, folder_path): """ Fills MaterialProperties from Material instance """ if material_properties is None or material is None: @@ -47,7 +77,7 @@ def fill_material_props(material : Material, material_properties): _fill_material_props_rendertype(material, material_properties) _fill_material_props_flags(material, material_properties) _fill_material_props_data(material, material_properties) - _fill_material_props_texture_maps(material, material_properties) + _fill_material_props_texture_maps(material, material_properties, folder_path) @@ -102,15 +132,17 @@ def _fill_material_props_data(material, material_properties): material_properties.detail_map_tiling_v = material.data[1] -def _fill_material_props_texture_maps(material, material_properties): +def _fill_material_props_texture_maps(material, material_properties, folder_path): - material_properties.texture_0 = material.texture0 - material_properties.texture_1 = material.texture1 - material_properties.texture_2 = material.texture2 - material_properties.texture_3 = material.texture3 + t0path = find_texture_path(folder_path, material.texture0) - material_properties.diffuse_map = material.texture0 - material_properties.distortion_map = material.texture1 - material_properties.normal_map = material.texture1 - material_properties.detail_map = material.texture2 - material_properties.environment_map = material.texture3 + material_properties.texture_0 = t0path if t0path else material.texture0 + material_properties.texture_1 = material.texture1 + material_properties.texture_2 = material.texture2 + material_properties.texture_3 = material.texture3 + + material_properties.diffuse_map = t0path + material_properties.distortion_map = material.texture1 + material_properties.normal_map = material.texture1 + material_properties.detail_map = material.texture2 + material_properties.environment_map = material.texture3 diff --git a/addons/io_scene_swbf_msh/msh_to_blend.py b/addons/io_scene_swbf_msh/msh_to_blend.py index 2ffd0d6..dbb6190 100644 --- a/addons/io_scene_swbf_msh/msh_to_blend.py +++ b/addons/io_scene_swbf_msh/msh_to_blend.py @@ -159,20 +159,7 @@ def extract_materials(folder_path: str, scene: Scene) -> Dict[str, bpy.types.Mat for material_name, material in scene.materials.items(): - new_mat = bpy.data.materials.new(name=material_name) - new_mat.use_nodes = True - bsdf = new_mat.node_tree.nodes["Principled BSDF"] - - diffuse_texture_path = find_texture_path(folder_path, material.texture0) - - if diffuse_texture_path: - texImage = new_mat.node_tree.nodes.new('ShaderNodeTexImage') - texImage.image = bpy.data.images.load(diffuse_texture_path) - new_mat.node_tree.links.new(bsdf.inputs['Base Color'], texImage.outputs['Color']) - - fill_material_props(material, new_mat.swbf_msh_mat) - - extracted_materials[material_name] = new_mat + extracted_materials[material_name] = swbf_material_to_blend(material_name, material, folder_path) return extracted_materials