"""Small opaque PBR materials, generated without external assets.""" import unreal from pathlib import Path from .paths import GENERATED, PROJECT, slug def color(name, rgb=(0.15, 0.5, 0.8), roughness=0.55, metallic=0.0): path = f"{GENERATED}/Materials/M_{slug(name)}" material = unreal.load_asset(path) if material is None: material = unreal.AssetToolsHelpers.get_asset_tools().create_asset( "M_" + name, GENERATED + "/Materials", unreal.Material, unreal.MaterialFactoryNew()) if not isinstance(material, unreal.Material): raise TypeError(f"{path} nie jest materialem.") lib = unreal.MaterialEditingLibrary lib.delete_all_material_expressions(material) material.set_editor_property("used_with_skeletal_mesh", True) base = lib.create_material_expression(material, unreal.MaterialExpressionConstant3Vector, -300, 0) base.set_editor_property("constant", unreal.LinearColor(*rgb, 1.0)) lib.connect_material_property(base, "", unreal.MaterialProperty.MP_BASE_COLOR) for offset, value, prop in [(160, roughness, unreal.MaterialProperty.MP_ROUGHNESS), (280, metallic, unreal.MaterialProperty.MP_METALLIC)]: node = lib.create_material_expression(material, unreal.MaterialExpressionConstant, -300, offset) node.set_editor_property("r", float(value)) lib.connect_material_property(node, "", prop) lib.recompile_material(material) unreal.EditorAssetLibrary.save_loaded_asset(material) return material def assign(actor, material, slot=None): """Set a material on an actor and all attached meshes; slot=None replaces every slot.""" components = [component for part in [actor, *actor.get_attached_actors(True, True)] for component in part.get_components_by_class(unreal.MeshComponent) if component.get_num_materials() > 0] if not components: raise TypeError("Material mozna przypisac do aktora z siatka.") for component in components: for index in range(component.get_num_materials()) if slot is None else [slot]: component.set_material(index, material) def texture_set(name, *, base_color, normal=None, roughness=None, metallic=None, emissive=None, tiling=1): """Create one atlas material from adjacent PBR textures (PNG/JPEG). tiling repeats the textures: one number, or (u, v) for separate directions. """ slug(name) u_tiling, v_tiling = tiling if isinstance(tiling, (tuple, list)) else (tiling, tiling) if u_tiling <= 0 or v_tiling <= 0: raise ValueError("tiling musi byc dodatnie.") tools = unreal.AssetToolsHelpers.get_asset_tools() path = f"{GENERATED}/Materials/M_{name}" material = unreal.load_asset(path) if material is None: material = tools.create_asset("M_" + name, GENERATED + "/Materials", unreal.Material, unreal.MaterialFactoryNew()) lib = unreal.MaterialEditingLibrary lib.delete_all_material_expressions(material) material.set_editor_property("used_with_skeletal_mesh", True) coordinates = lib.create_material_expression(material, unreal.MaterialExpressionTextureCoordinate, -600, 0) coordinates.set_editor_property("u_tiling", float(u_tiling)) coordinates.set_editor_property("v_tiling", float(v_tiling)) entries = [("BaseColor", base_color, unreal.MaterialProperty.MP_BASE_COLOR, "RGB", "color"), ("Normal", normal, unreal.MaterialProperty.MP_NORMAL, "RGB", "normal"), ("Roughness", roughness, unreal.MaterialProperty.MP_ROUGHNESS, "R", "linear"), ("Metallic", metallic, unreal.MaterialProperty.MP_METALLIC, "R", "linear"), ("Emissive", emissive, unreal.MaterialProperty.MP_EMISSIVE_COLOR, "RGB", "color")] for index, (label, filename, prop, channel, role) in enumerate(entries): if filename is None: if label in {"Roughness", "Metallic"}: node = lib.create_material_expression(material, unreal.MaterialExpressionConstant, -300, index * 180) node.set_editor_property("r", 0.6 if label == "Roughness" else 0.0) lib.connect_material_property(node, "", prop) continue source = Path(filename).expanduser() source = (source if source.is_absolute() else PROJECT / source).resolve() if not source.is_file(): raise FileNotFoundError(source) task = unreal.AssetImportTask() task.filename = str(source) task.destination_path = f"{GENERATED}/Materials/Textures/{name}" task.destination_name = "T_" + label task.automated = True task.replace_existing = True task.save = True task.set_editor_property("async_", False) tools.import_asset_tasks([task]) textures = [obj for obj in task.get_objects() if isinstance(obj, unreal.Texture2D)] if len(textures) != 1: raise RuntimeError("Nie mozna zaimportowac tekstury " + str(source)) texture = textures[0] texture.set_editor_property("srgb", role == "color") texture.set_editor_property("compression_settings", unreal.TextureCompressionSettings.TC_NORMALMAP if role == "normal" else unreal.TextureCompressionSettings.TC_MASKS if role == "linear" else unreal.TextureCompressionSettings.TC_DEFAULT) unreal.EditorAssetLibrary.save_loaded_asset(texture) node = lib.create_material_expression(material, unreal.MaterialExpressionTextureSample, -300, index * 180) node.set_editor_property("texture", texture) node.set_editor_property("sampler_type", unreal.MaterialSamplerType.SAMPLERTYPE_NORMAL if role == "normal" else unreal.MaterialSamplerType.SAMPLERTYPE_MASKS if role == "linear" else unreal.MaterialSamplerType.SAMPLERTYPE_COLOR) lib.connect_material_expressions(coordinates, "", node, "UVs") lib.connect_material_property(node, channel, prop) lib.recompile_material(material) unreal.EditorAssetLibrary.save_loaded_asset(material) return material