#!/usr/bin/env python3 """Audit the DemoMap for staged actors that would render wrong. Checks that are cheap and catch the failures this project actually had: 1. **No unmaterialed actors.** 604 actors once rendered with the default material because the demo builder deleted and recreated the site materials, orphaning every reference; the map looked merely "a bit flat". 2. **Placement spans.** Each verified frontage building is probed against `Tools/ue_placement.py`; a building 13 m off its footprint still renders fine, which is exactly why it needs a number, not an eye. 3. **No duplicate actor labels.** Two buildings' kit pieces sharing a label made the outliner ambiguous and hid one of them. UnrealEditor-Cmd -run=pythonscript \ -script=Tools/ue_audit_scene.py -nullrhi -unattended -nopause -stdout Result file: `/tmp/gigline/scene-audit.txt`. Exit code from the commandlet is always 0, so **read the file** — `visual_loop.py --verify` does not depend on it. """ from __future__ import annotations import collections import os import sys import unreal sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import ue_placement # noqa: E402 MAP = "/Game/Hollywood/DemoMap" RESULT = "/tmp/gigline/scene-audit.txt" lines: list[str] = [] failures = 0 def log(message: str) -> None: lines.append(message) print(message) def fail(message: str) -> None: global failures failures += 1 log("FAIL " + message) def main() -> None: if not unreal.EditorLevelLibrary.load_level(MAP): log(f"could not load {MAP}") return actors = unreal.EditorLevelLibrary.get_all_level_actors() # 1. materials unmaterialed = [] for actor in actors: comp = actor.get_component_by_class(unreal.StaticMeshComponent) if not comp or not comp.static_mesh: continue if comp.get_material(0) is None: unmaterialed.append(actor.get_actor_label()) log(f"mesh actors: {sum(1 for a in actors if a.get_component_by_class(unreal.StaticMeshComponent))}") if unmaterialed: fail(f"{len(unmaterialed)} actor(s) with no material, e.g. {unmaterialed[:6]}") else: log("OK every staged mesh has a material") # 2. placement spans for actor in actors: label = actor.get_actor_label() key = None if label.startswith("Frontage_SM_"): name = label[len("Frontage_SM_"):] key = {"Hollywood_6922": "6922", "Hollywood_6902": "6902", "ElCapitanTheatre": "6838", "Budget_6822": "6822", "Hollywood_6806": "6806", "LeeDrug_6800": "6800"}.get(name) elif label == ue_placement.HERO_ACTOR: key = "hero" if not key: continue origin, extent = actor.get_actor_bounds(False) lo, hi = origin.x - extent.x, origin.x + extent.x problem = ue_placement.describe_span_failure(key, lo, hi) if problem: fail(problem) else: log(f"OK {key}: X {lo:.0f}..{hi:.0f}") # 3. duplicate labels counts = collections.Counter(a.get_actor_label() for a in actors) duplicates = {k: v for k, v in counts.items() if v > 1} if duplicates: sample = list(duplicates.items())[:6] fail(f"{len(duplicates)} duplicated actor label(s), e.g. {sample}") else: log("OK no duplicate actor labels") log(f"== {failures} failure(s) ==") try: main() except Exception as exc: # noqa: BLE001 - surface in the result file import traceback log(f"FAILED: {type(exc).__name__}: {exc}") log(traceback.format_exc()) os.makedirs(os.path.dirname(RESULT), exist_ok=True) with open(RESULT, "w", encoding="utf-8") as handle: handle.write("\n".join(lines) + "\n")