# Import the two north-side background buildings (TCL Chinese Theatre, Madame # Tussaud's) into Unreal and stage them in the demo map, across the street from # the hero facade. # # Placement: these blockout meshes live in the same local frame as the site # meshes (X east / Y north / Z up, metres, origin Hollywood x Highland), which # are imported at the Unreal origin and land correctly via the `ue_metric_v1` # FBX axis mapping (north -> -Y). So the buildings are spawned at Unreal # (0,0,0) like the site meshes, not at the pivot-shifted hero facade position. # # UnrealEditor-Cmd -run=pythonscript \ # -script=/Tools/ue_import_north_buildings.py -nullrhi -unattended -nopause -stdout import os import sys import unreal sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import ue_common # noqa: E402 SRC = "/tmp/gigline/north" DEST = "/Game/Hollywood/North" LEVEL = "/Game/Hollywood/DemoMap" RESULT = "/tmp/gigline/north-import.txt" lines = [] def log(msg): lines.append(str(msg)) unreal.log(str(msg)) IMPORTS = [ ("TCL_Chinese_Theatre.fbx", "SM_TCL_Chinese_Theatre"), ("Madame_Tussauds.fbx", "SM_Madame_Tussauds"), ] def import_meshes(): assets = unreal.EditorAssetLibrary.list_assets(DEST) if unreal.EditorAssetLibrary.does_directory_exist(DEST) else [] for fn, asset_name in IMPORTS: unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([unreal.AssetImportTask( filename=os.path.join(SRC, fn), destination_path=DEST, destination_name=asset_name, automated=True, save=True, replace_existing=True, options=unreal.FbxImportUI( import_mesh=True, import_materials=True, import_textures=False, import_as_skeletal=False) if False else None, )]) # FbxImportUI kwargs are unreliable across 5.8; configure explicitly below. def stage(): if unreal.EditorAssetLibrary.does_asset_exist(LEVEL): unreal.EditorLevelLibrary.load_level(LEVEL) for actor in list(unreal.EditorLevelLibrary.get_all_level_actors()): if actor.get_actor_label().startswith("North_"): unreal.EditorLevelLibrary.destroy_actor(actor) for asset_name, label in (("SM_TCL_Chinese_Theatre", "North_TCL_Chinese"), ("SM_Madame_Tussauds", "North_Madame_Tussauds")): mesh = unreal.EditorAssetLibrary.load_asset(f"{DEST}/{asset_name}") if mesh is None: log(f"MISSING {DEST}/{asset_name}") continue actor = unreal.EditorLevelLibrary.spawn_actor_from_class( unreal.StaticMeshActor, unreal.Vector(0, 0, 0)) actor.set_actor_label(label) comp = actor.static_mesh_component comp.set_static_mesh(mesh) comp.set_mobility(unreal.ComponentMobility.STATIC) # Report where the mesh actually lands in world space (site-frame → Unreal). box = mesh.get_bounding_box() mn, mx = box.min, box.max log(f"{label}: mesh world-space X {mn.x:.0f}..{mx.x:.0f} Y {mn.y:.0f}..{mx.y:.0f} size " f"{mx.x-mn.x:.0f}x{mx.y-mn.y:.0f} cm") _make_map_writable(LEVEL) saved = unreal.EditorLoadingAndSavingUtils.save_map( unreal.EditorLevelLibrary.get_editor_world(), LEVEL) log(f"save_map {LEVEL}: {saved}") if not ue_common.require_saved(saved, LEVEL): log(f"FAILED to save {LEVEL} — staged content will not persist") def _make_map_writable(level: str) -> None: """Clear the read-only bit an LFS .umap checks out with. Without this `save_map()` returns False and the staged actor is lost on the next load — no exception, no non-zero exit (see MEMORY: 0.1.x trap). """ content = unreal.Paths.project_content_dir() rel = level[len("/Game/"):] if level.startswith("/Game/") else level umap = os.path.join(content, rel + ".umap") note = ue_common.make_writable(umap) if note: log(f"map writable: {note}") def main(): for fn, asset_name in IMPORTS: task = unreal.AssetImportTask() task.filename = os.path.join(SRC, fn) task.destination_path = DEST task.destination_name = asset_name task.automated = True task.save = True task.replace_existing = True ui = unreal.FbxImportUI() ui.import_mesh = True ui.import_materials = True ui.import_textures = False ui.import_as_skeletal = False smd = ui.static_mesh_import_data smd.import_uniform_scale = 1.0 smd.combine_meshes = True smd.convert_scene_unit = False smd.force_front_x_axis = False smd.normal_import_method = unreal.FBXNormalImportMethod.FBXNIM_IMPORT_NORMALS task.options = ui unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task]) log(f"imported {asset_name}") unreal.EditorLoadingAndSavingUtils.save_dirty_packages(True, True) stage() try: main() except Exception as exc: # noqa import traceback log(f"FAILED: {type(exc).__name__}: {exc}") log(traceback.format_exc()) with open(RESULT, "w") as f: f.write("\n".join(lines) + "\n")