# Import the Blender site meshes (terrain, road, sidewalks, kerbs) into Unreal. # # blender ... --python Art/blender/site/export_site_fbx.py -- --out /tmp/gigline/site # /.../UnrealEditor-Cmd -run=pythonscript \ # -script=/Tools/ue_import_site.py -nullrhi -unattended -nopause -stdout # # Each mesh is a separate FBX because they carry different materials and want # different collision settings (terrain and street are walkable surfaces). # Destination: /Game/Hollywood/Site/. # # Y-axis note: the FBX preset maps Blender +Y (north) to Unreal -Y, so the site # arrives in the same frame as the hero facade. Nothing is mirrored here. import glob import os import unreal SRC = "/tmp/gigline/site" DEST = "/Game/Hollywood/Site" RESULT = "/tmp/gigline/site-import.txt" lines = [] def log(msg): lines.append(str(msg)) unreal.log(str(msg)) def import_fbx(path, name, collision="complex"): # 5.8 note: there is no FBX import 'no collision' switch exposed to Python; # a default import simply generates no collision primitives, so collision="none" # means: skip the trace-flag step below and leave the body setup empty. task = unreal.AssetImportTask() task.filename = path task.destination_path = DEST task.destination_name = 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]) mesh = unreal.EditorAssetLibrary.load_asset(f"{DEST}/{name}") if mesh is None: log(f"MISSING after import: {DEST}/{name}") return # Collision is requested at import time so the 0.1.0 walkability check has # something to stand on. Terrain and street get complex-as-simple: a simple # hull would ramp straight over the kerbs. # # UE 5.8 exposes this only through the body setup's trace flag (the mesh itself # has no trace-flag property, and the subsystem has no setter in Python), so # the import path sets the body setup's trace flag, then verifies it by reading # it back. if collision == "none": box = mesh.get_bounding_box() mn, mx = box.min, box.max log(f"{name:18} import ok size=({mx.x - mn.x:.0f}, {mx.y - mn.y:.0f}, {mx.z - mn.z:.0f}) cm " f"min_z={mn.z:.1f} collision=none") return trace = (unreal.CollisionTraceFlag.CTF_USE_COMPLEX_AS_SIMPLE if collision == "complex" else unreal.CollisionTraceFlag.CTF_USE_SIMPLE_AND_COMPLEX) try: body = mesh.get_editor_property("body_setup") body.set_editor_property("collision_trace_flag", trace) mesh.set_editor_property("body_setup", body) except Exception as exc: # noqa: BLE001 - report, do not fail the whole import log(f"{name}: could not set trace flag ({exc}); leaving the default") box = mesh.get_bounding_box() mn, mx = box.min, box.max log(f"{name:18} import ok size=({mx.x - mn.x:.0f}, {mx.y - mn.y:.0f}, {mx.z - mn.z:.0f}) cm " f"min_z={mn.z:.1f} collision={collision}") def main(): files = sorted(glob.glob(os.path.join(SRC, "*.fbx"))) if not files: log(f"no FBX in {SRC}; run Art/blender/site/export_site_fbx.py first") return for path in files: stem = os.path.splitext(os.path.basename(path))[0] # Crossing markings are paint on the road surface — no collision at all, # so the player never stands on a 1 cm lip at the band edges. if stem == "Crossing_Markings": import_fbx(path, stem, "none") continue collision = "complex" if stem in ("Terrain_DEM", "Road_Hollywood", "Sidewalk_South", "Sidewalk_North") else "simple" import_fbx(path, stem, collision) unreal.EditorLoadingAndSavingUtils.save_dirty_packages(True, True) try: main() except Exception as exc: # noqa: BLE001 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")