Created
October 6, 2026 18:10
-
-
Save NathoSteveo/b80cb0eb7c9cb23a64af3bd0a31524f6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import unreal | |
| # ---- CONFIG ---- | |
| INSTANCE_PREFIX = "MI_" | |
| TEXTURE_PREFIX = "T_" | |
| # texture suffix -> parent material parameter name (case-sensitive!) | |
| PARAMS = {"D": "D", "N": "N", "R": "R", "M": "M"} | |
| # ---------------- | |
| eal = unreal.EditorAssetLibrary | |
| mel = unreal.MaterialEditingLibrary | |
| def get_selected_folder(): | |
| """Return the folder selected in the Content Browser.""" | |
| folders = unreal.EditorUtilityLibrary.get_selected_folder_paths() | |
| if folders: | |
| path = folders[0] | |
| # UE5 can return paths like "/All/Game/..." | |
| if path.startswith("/All/"): | |
| path = path[4:] | |
| return path | |
| # Fallback: use the folder of the first selected asset | |
| assets = unreal.EditorUtilityLibrary.get_selected_assets() | |
| if assets: | |
| pkg = assets[0].get_path_name().split(".")[0] | |
| return pkg.rsplit("/", 1)[0] | |
| return None | |
| def pkg_path(asset_path): | |
| # "/Game/Folder/Name.Name" -> "/Game/Folder/Name" | |
| return asset_path.split(".")[0] | |
| def configure_texture(tex, suffix): | |
| """Set sRGB / compression to match the texture's role.""" | |
| if suffix == "D": | |
| tex.set_editor_property("srgb", True) | |
| tex.set_editor_property("compression_settings", unreal.TextureCompressionSettings.TC_DEFAULT) | |
| elif suffix == "N": | |
| tex.set_editor_property("srgb", False) | |
| tex.set_editor_property("compression_settings", unreal.TextureCompressionSettings.TC_NORMALMAP) | |
| else: # R, M | |
| tex.set_editor_property("srgb", False) | |
| tex.set_editor_property("compression_settings", unreal.TextureCompressionSettings.TC_MASKS) | |
| eal.save_loaded_asset(tex) | |
| def run(): | |
| import_folder = get_selected_folder() | |
| if not import_folder: | |
| unreal.log_error("Select a folder (or an asset) in the Content Browser first.") | |
| return | |
| unreal.log(f"Processing folder: {import_folder}") | |
| all_paths = eal.list_assets(import_folder, recursive=True, include_folder=False) | |
| # index textures and instances by name | |
| textures, instances = {}, [] | |
| for p in all_paths: | |
| asset = eal.load_asset(p) | |
| if isinstance(asset, unreal.Texture2D): | |
| textures[asset.get_name()] = asset | |
| elif isinstance(asset, unreal.MaterialInstanceConstant): | |
| instances.append(asset) | |
| for mi in instances: | |
| old_name = mi.get_name() | |
| base = old_name[len(INSTANCE_PREFIX):] if old_name.startswith(INSTANCE_PREFIX) else old_name | |
| # assign textures | |
| for suffix, param in PARAMS.items(): | |
| tex = textures.get(f"{TEXTURE_PREFIX}{base}_{suffix}") | |
| if tex: | |
| configure_texture(tex, suffix) | |
| mel.set_material_instance_texture_parameter_value(mi, param, tex) | |
| else: | |
| unreal.log_warning(f"[{base}] missing texture {TEXTURE_PREFIX}{base}_{suffix}") | |
| mel.update_material_instance(mi) | |
| eal.save_loaded_asset(mi) | |
| # rename last (rename_asset fixes references, e.g. on the static mesh) | |
| if not old_name.startswith(INSTANCE_PREFIX): | |
| old_path = pkg_path(mi.get_path_name()) | |
| folder = old_path.rsplit("/", 1)[0] | |
| new_path = f"{folder}/{INSTANCE_PREFIX}{base}" | |
| if not eal.rename_asset(old_path, new_path): | |
| unreal.log_warning(f"Could not rename {old_path}") | |
| unreal.log(f"Processed {len(instances)} material instances") | |
| run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment