# Rebuild the vehicle's materials from the Blender colours (0.3.0). # # blender -b "Art/blender/vehicle/hot hatch.blend" \ # --python Tools/vehicle_from_template.py -- --out /tmp/gigline/hatch # /Engine/Binaries/Linux/UnrealEditor-Cmd -run=pythonscript \ # -script=/Tools/ue_vehicle_materials.py -nullrhi -unattended -nopause -stdout # # Why this exists: the template's car paint is a **legacy node graph**, so its colour never # reached the exported FBX and the car arrived in Unreal near-black while Blender showed it # red. `Tools/vehicle_from_template.py` now reads those graphs and records the colours; this # replaces the imported material assets in place with plain constant-colour materials, so # the mesh slots pick them up by path without anything being re-imported. # # Result file: /tmp/gigline/vehicle-materials.txt import json import os import unreal DEST = "/Game/Hollywood/Vehicle" _REPO = os.path.abspath(os.path.join(unreal.Paths.project_dir(), "..", "..")) REPORT = os.path.join(_REPO, "..", "..", "..", "tmp", "gigline", "hatch", "report.json") REPORT = "/tmp/gigline/hatch/report.json" RESULT = "/tmp/gigline/vehicle-materials.txt" lines = [] def log(msg): lines.append(str(msg)) unreal.log(str(msg)) def existing_material_name(name): """Find the asset the importer actually created for a Blender material name. Blender names may contain spaces ("car paint"), which are not valid asset names, so the importer sanitises them. Guessing the sanitised form is fragile; this searches the folder instead and reports when nothing matches. """ registry = unreal.AssetRegistryHelpers.get_asset_registry() wanted = name.lower().replace(" ", "").replace("_", "") for asset in registry.get_assets_by_path(DEST, recursive=False): candidate = str(asset.asset_name).lower().replace(" ", "").replace("_", "") if candidate == wanted: return str(asset.asset_name) return None def replace_material(name, spec): resolved = existing_material_name(name) if resolved is None: log(f"{name}: no imported asset matches - skipped") return None name = resolved full = f"{DEST}/{name}" if unreal.EditorAssetLibrary.does_asset_exist(full): unreal.EditorAssetLibrary.delete_asset(full) tools = unreal.AssetToolsHelpers.get_asset_tools() material = tools.create_asset(name, DEST, unreal.Material, unreal.MaterialFactoryNew()) if material is None: log(f"{name}: could not create") return None lib = unreal.MaterialEditingLibrary colour = spec["base_color"] base = lib.create_material_expression(material, unreal.MaterialExpressionConstant3Vector, -400, 0) base.set_editor_property("constant", unreal.LinearColor(colour[0], colour[1], colour[2], 1.0)) lib.connect_material_property(base, "", unreal.MaterialProperty.MP_BASE_COLOR) rough = lib.create_material_expression(material, unreal.MaterialExpressionConstant, -400, 180) rough.set_editor_property("r", max(0.05, float(spec.get("roughness", 0.5)))) lib.connect_material_property(rough, "", unreal.MaterialProperty.MP_ROUGHNESS) metallic = lib.create_material_expression(material, unreal.MaterialExpressionConstant, -400, 260) metallic.set_editor_property("r", float(spec.get("metallic", 0.0))) lib.connect_material_property(metallic, "", unreal.MaterialProperty.MP_METALLIC) lib.recompile_material(material) log(f"{name}: base {tuple(round(c, 3) for c in colour)} " f"roughness {spec.get('roughness')} metallic {spec.get('metallic')} " f"({spec.get('source')})") return material def main(): with open(REPORT) as handle: report = json.load(handle) materials = report.get("materials", {}) log(f"{len(materials)} materials in the Blender report") made = 0 for name, spec in materials.items(): if replace_material(name, spec): made += 1 unreal.EditorLoadingAndSavingUtils.save_dirty_packages(True, True) lines.append(f"VERDICT: {'PASS' if made == len(materials) else 'FAIL'}") log(f"{made}/{len(materials)} materials replaced") try: main() except Exception as exc: # noqa: BLE001 import traceback log(f"FAILED: {type(exc).__name__}: {exc}") log(traceback.format_exc()) lines.append("VERDICT: FAIL") with open(RESULT, "w") as f: f.write("\n".join(lines) + "\n")