# Imports the hero façade FBX into Unreal with the named preset `ue_metric_v1` # and places it at the measured site position. # # /Engine/Binaries/Linux/UnrealEditor-Cmd -run=pythonscript \ # -script=/Tools/ue_hero_facade_import.py -nullrhi -unattended -nopause -stdout # # Placement follows Geo/manifests/local-origin.md and the blockout frame: # project X = UTM easting - 376515.389 (east +) # project Y = -(UTM northing - 3774224.083) in Unreal, because the FBX preset # maps Blender +Y (north) to Unreal -Y and Blender +X to Unreal +X. The building # line of the hero block sits at Unreal Y = -1450 cm (14.5 m south of the road # centreline, from the street profile), and the hero's east edge at # X = -12024.5 cm (projected control point in local-origin.md). import unreal FBX = "/tmp/gigline/hero-facade.fbx" DEST = "/Game/Hollywood/ElCapitan" ACTOR_LABEL = "Hero_Facade" LEVEL = "/Game/Hollywood/DemoMap" # Unreal centimetres: south-east corner of the façade, on the building line. PLACEMENT_CM = (-12024.5, -1450.0, 0.0) RESULT = "/tmp/gigline/hero-facade-import.txt" lines = [] def log(msg): lines.append(str(msg)) def import_fbx(): task = unreal.AssetImportTask() task.filename = FBX task.destination_path = DEST task.destination_name = "SM_HeroFacade" 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]) unreal.EditorLoadingAndSavingUtils.save_dirty_packages(True, True) def measure_and_place(): asset = unreal.EditorAssetLibrary.load_asset(f"{DEST}/SM_HeroFacade") if asset is None: log(f"MISSING {DEST}/SM_HeroFacade") return box = asset.get_bounding_box() mn, mx = box.min, box.max log(f"SM_HeroFacade bounds cm: size=({mx.x - mn.x:.1f}, {mx.y - mn.y:.1f}, {mx.z - mn.z:.1f}) " f"min=({mn.x:.1f}, {mn.y:.1f}, {mn.z:.1f})") # StaticMaterial is a struct wrapper: the material is its material_interface. names = [] for sm in asset.get_editor_property("static_materials"): mat = sm.get_editor_property("material_interface") names.append(mat.get_name() if mat else "None") log(f"materials: {names}") # Place on the demo map if it exists; otherwise create it from the empty level. if unreal.EditorAssetLibrary.does_asset_exist(LEVEL): unreal.EditorLevelLibrary.load_level(LEVEL) else: if not unreal.EditorLevelLibrary.new_level(LEVEL): log(f"could not create {LEVEL}") return for actor in unreal.EditorLevelLibrary.get_all_level_actors(): if actor.get_actor_label() == ACTOR_LABEL: unreal.EditorLevelLibrary.destroy_actor(actor) actor = unreal.EditorLevelLibrary.spawn_actor_from_class( unreal.StaticMeshActor, unreal.Vector(*PLACEMENT_CM)) actor.set_actor_label(ACTOR_LABEL) comp = actor.static_mesh_component comp.set_static_mesh(asset) comp.set_mobility(unreal.ComponentMobility.STATIC) log(f"placed {ACTOR_LABEL} at {PLACEMENT_CM} (cm) on {LEVEL}") saved = unreal.EditorLoadingAndSavingUtils.save_map( unreal.EditorLevelLibrary.get_editor_world(), LEVEL) log(f"save_map {LEVEL}: {saved}") try: import_fbx() measure_and_place() except Exception as exc: # noqa: BLE001 import traceback log(f"FAILED: {type(exc).__name__}: {exc}") log(traceback.format_exc()) with open(RESULT, "w") as handle: handle.write("\n".join(lines) + "\n")