review: restore character + cubes in ReviewMap, re-render all shots

Root cause of the empty review frames: EditorLevelLibrary.load_level/new_level
are deprecated no-ops in -nullrhi commandlets, so staged actors landed in the
current world and got saved over the wrong map. Switched both map builders to
the LevelEditorSubsystem and reordered the review builder to save ReviewMap
before the DemoMap section (the switch-back to a map that only exists in
memory silently failed, writing demo content over ReviewMap).

Rebuilt both maps, re-rendered every screenshot; user visual check passed.
Screenshots/README.md cube naming corrected (A grounded, B floating), approval
states updated; TODO.md visual-review notes closed. MEMORY.md records the
deprecated-call and save-order traps.
This commit is contained in:
zwitschi committed 2026-11-07 16:23:59 +01:00
1 parent ab24ef3b24
commit c9e933376c
22 files changed
+92 -69

No files matched your search

+12 -2
View File
@@ -24,6 +24,16 @@ RESULT = "/tmp/gigline/demo-map.txt"
lines = []
# EditorLevelLibrary.load_level/new_level are deprecated no-ops in -nullrhi
# commandlets (they return without switching worlds, so staged actors land in
# whatever world is current and get saved over the wrong map). The
# LevelEditorSubsystem actually switches.
LES = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
def switch_level(path):
return LES.load_level(path)
def log(msg):
lines.append(str(msg))
unreal.log(str(msg))
@@ -53,11 +63,11 @@ def clear(prefixes=()):
def build():
# --- level ------------------------------------------------------------
if unreal.EditorAssetLibrary.does_asset_exist(MAP):
if not unreal.EditorLevelLibrary.load_level(MAP):
if not switch_level(MAP):
log(f"could not load {MAP}")
return
else:
if not unreal.EditorLevelLibrary.new_level(MAP):
if not switch_level(MAP):
log(f"could not create {MAP}")
return
+33 -27
View File
@@ -21,6 +21,16 @@ MAP_PATH = "/Game/Test/ReviewMap"
lines = []
# EditorLevelLibrary.load_level/new_level are deprecated no-ops in -nullrhi
# commandlets (they return without switching worlds, so staged actors land in
# whatever world is current and get saved over the wrong map). The
# LevelEditorSubsystem actually switches.
LES = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
def switch_level(path):
return LES.load_level(path)
def log(msg):
lines.append(str(msg))
@@ -211,13 +221,33 @@ try:
# oblique from the north-west, reading the colonnade in depth
("hero-facade-oblique", unreal.Vector(hero_x - 2200, -3400, 620), unreal.Vector(hero_x + 300, hero_y, 700)),
]
# --- review shot cameras + save -------------------------------------------
# Order matters: the review scene is staged in the default (unsaved) world.
# Save it as ReviewMap BEFORE touching DemoMap — switching back to a map
# that only exists in memory fails silently, and the final save would then
# write the demo world over ReviewMap (this exact corruption happened once).
shots = spawn(unreal.GigLineShotActor, unreal.Vector(0, 0, 300), "ShotSequencer")
if shots:
shots.set_editor_property("shot_locations", [c[1] for c in cams])
shots.set_editor_property(
"shot_rotations",
[unreal.MathLibrary.find_look_at_rotation(c[1], c[2]) for c in cams])
shots.set_editor_property("shot_names", [c[0] for c in cams])
shots.set_editor_property("quit_when_done", True)
log(f"staged: ShotSequencer with {len(cams)} cameras: {', '.join(c[0] for c in cams)}")
ensure_writable(MAP_PATH)
saved = unreal.EditorLoadingAndSavingUtils.save_map(
unreal.EditorLevelLibrary.get_editor_world(), MAP_PATH)
log(f"save_map {MAP_PATH}: {saved}")
# --- demo map views: what the player actually sees ----------------------
demo = unreal.EditorAssetLibrary.load_asset("/Game/Hollywood/DemoMap")
if demo and unreal.EditorAssetLibrary.does_asset_exist("/Game/Hollywood/DemoMap"):
# The demo map has terrain, street, sidewalks and the facade; the review
# floor does not. Load it, stage the shot cameras, save, then restore the
# review map so the rest of the run is unaffected.
unreal.EditorLevelLibrary.load_level("/Game/Hollywood/DemoMap")
# floor does not. Load it, stage the shot cameras, save. No switch back:
# the review map is already saved, and the run ends here.
switch_level("/Game/Hollywood/DemoMap")
for actor in unreal.EditorLevelLibrary.get_all_level_actors():
if actor.get_actor_label() == "ShotSequencer":
unreal.EditorLevelLibrary.destroy_actor(actor)
@@ -247,34 +277,10 @@ try:
saved = unreal.EditorLoadingAndSavingUtils.save_map(
unreal.EditorLevelLibrary.get_editor_world(), "/Game/Hollywood/DemoMap")
log(f"demo map staged: {len(demo_shots)} cameras, save {saved}")
unreal.EditorLevelLibrary.load_level("/Game/Test/ReviewMap")
for actor in unreal.EditorLevelLibrary.get_all_level_actors():
if actor.get_actor_label() == "ShotSequencer":
unreal.EditorLevelLibrary.destroy_actor(actor)
else:
log("MISSING: /Game/Hollywood/DemoMap (run Tools/ue_build_demo_map.py)")
shots = spawn(unreal.GigLineShotActor, unreal.Vector(0, 0, 300), "ShotSequencer")
if shots:
shots.set_editor_property("shot_locations", [c[1] for c in cams])
shots.set_editor_property(
"shot_rotations",
[unreal.MathLibrary.find_look_at_rotation(c[1], c[2]) for c in cams])
shots.set_editor_property("shot_names", [c[0] for c in cams])
shots.set_editor_property("quit_when_done", True)
log(f"staged: ShotSequencer with {len(cams)} cameras: {', '.join(c[0] for c in cams)}")
except Exception as exc: # noqa
log(f"staging failed: {exc!r}")
# --- save --------------------------------------------------------------------
try:
ensure_writable(MAP_PATH)
world = unreal.EditorLevelLibrary.get_editor_world()
saved = unreal.EditorLoadingAndSavingUtils.save_map(world, MAP_PATH)
log(f"save_map {MAP_PATH}: {saved}")
except Exception as exc: # noqa
log(f"save_map failed: {exc!r}")
with open(RESULT, "w") as f:
f.write("\n".join(lines) + "\n")