Merged PR #32: 0.1.5 — hero detail, scene audit, material orphaning fixed Co-authored-by: Zwitschi <zwitschi82@gmail.com>
115 lines
3.7 KiB
Python
115 lines
3.7 KiB
Python
#!/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 <uproject> -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")
|