5 Commits
Author SHA1 Message Date
zwitschi e1ddf852fa docs(agents): document the Unreal Engine and C++ skill packs
AGENTS.md only described the Blender packs. Add the two skill sets that shipped
with the project since:

- 31 Unreal Engine packs (MIT, quodsoler/unreal-engine-skills f3742d7) at
  .agents/skills/ue-*/, targeting UE 5.8 C++, with ue-project-context as the entry
  point and a discipline mapping to this project's work.
- The cpp router (MIT, DiamonDinoia/skill-cpp f2617fb) at .agents/skills/cpp/, for
  general C++11-23 against the Core Guidelines; the project is C++20.
2026-11-10 17:51:01 +01:00
zwitschi 8e6aa9cff2 docs(vehicle): record the float fix is fragile — delete+recreate, not the box centre
The transient settle at Z 78 was the first physics-asset recreation; repeating the
delete + rebuild + assign cycle flipped the pawn to simulating=0 and stuck at spawn,
with the same box centre -18 that had settled. The delete+recreate is the failure,
not the box: delete_asset does not fully release the package, so NewObject reuses
stale state and the rebuilt body stops simulating.

- Plugin enablement (5e328f5) stays the solid fix (4 wheels).
- The physics asset needs a proper fix: set PhysicsType=PhysType_Simulated and update
  in place, not delete+recreate.
- ue_vehicle_import.py SOURCE is stale (/tmp/gigline/hatch vs /tmp/gigline/vehicle for
  the skeletal FBX), so the wired regeneration is never reached.
- Driving is still untested.
2026-11-10 17:35:20 +01:00
zwitschi 5e328f5d3e fix(vehicle): enable ChaosVehiclesPlugin so the Chaos vehicle is actually created
The Chaos vehicle never existed: ChaosVehiclesPlugin is EnabledByDefault=false and
was absent from GigLine.uproject, so FChaosVehicleManager was null and
OnCreatePhysicsState skipped CreateVehicle() (read from the engine source). The
pawn stayed at setups=4 wheels=0 output=null and the mesh fell back to the regular
rigid-body solver, which is what teleported upward.

- Enable ChaosVehiclesPlugin in GigLine.uproject. CreateVehicle() now runs:
  'wheels 4', 'output present', 'setups=4 wheels=4'.
- Drop the constructor's SetSimulatePhysics(true) / collision-profile override: the
  Chaos movement component drives the mesh through async physics, and forcing the
  regular solver kept the body out of the vehicle solver.

The plugin fix turns the teleport into a stable 'float at Z 545' (540 cm above the
floor) in both probe maps - suspension tuning is the next bug.
2026-11-10 16:39:42 +01:00
zwitschi 620cbe181c docs(vehicle): fresh probe data — the writer is the physics sim, asset pipeline ruled out
Rebuilt and re-ran the vehicle probe workflow:

- Control (no terrain) falls and settles (300 -> 78 cm).
- Terrain rises (313 -> 545 cm) with body velocity pinned to 0 and zero overlaps.
- Turning physics OFF at 3 s stops the rise, so the Chaos vehicle solver is
  teleporting the body (transform set directly, no velocity) - not an external
  SetActorLocation.
- 0 wheels in both runs, so the missing wheels are not the trigger; the terrain is
  what flips fall to rise.

Ruled out the asset pipeline with commandlets: the FBX, the imported
SK_Vehicle_Sedan (476x228x144 cm), the physics asset (452x205x40) and Terrain_DEM
(380x210 m) are all correct; the logged '1 cm bounds' is a stale component read,
not a bad mesh.

Next: bisect step 2 - a stock AWheeledVehiclePawn beside AGigLineVehicle in the
terrain probe map.
2026-11-10 16:07:39 +01:00
zwitschi 66ca685059 fix: change order in screenshot capture 2026-11-10 15:47:12 +01:00
6 changed files with 173 additions and 41 deletions

No files matched your search

+27
View File
@@ -197,6 +197,33 @@ Do not copy the common packs into the repository: a second copy under a differen
**Caveat:** the skill packs assume MCP-driven interactive Blender. This project runs Blender headless via scripts in `Tools/` — use the skills as method guides (checklists, naming conventions, proportions, validation steps), not for their MCP tooling. The headless scripts remain the execution path.
## Unreal Engine skills
The 31 Unreal Engine skill packs (MIT, [quodsoler/unreal-engine-skills](https://github.com/quodsoler/unreal-engine-skills) `f3742d7`) live in the repository at `.agents/skills/ue-*/` and target **UE 5.8 C++**. Each carries API examples verified against the 5.8 headers plus a deprecated-API table that maps old calls to the 5.8 forms. They are agent instructions, not add-ons.
`.agents/skills/ue-project-context/` is the entry point: it writes `.agents/ue-project-context.md` (module names, conventions, enabled plugins), and every other UE skill reads that file first. Refresh it when the project changes — new module, new plugin, engine bump — before consulting the rest.
The general mapping to this project's work:
- **Build and modules** — `ue-module-build-system` (Build.cs, unresolved externals, include order, plugin enablement).
- **Reflection and core types** — `ue-cpp-foundations` (UCLASS/UPROPERTY/UFUNCTION, TObjectPtr, delegates), `ue-blueprint-cpp-interop`.
- **Actor/component lifecycle** — `ue-actor-component-architecture` (spawning, attachment, tick groups, EndPlay).
- **Player and pawns** — `ue-character-movement` (the character's walking), `ue-gameplay-framework` (GameMode/Pawn/Controller), `ue-input-system` (Enhanced Input). The Chaos vehicle is the `ChaosVehiclesPlugin` — see the vehicle notes in `TODO.md`, not a skill pack.
- **Physics and collision** — `ue-physics-collision` (channels, traces, overlaps, profiles).
- **Materials and rendering** — `ue-materials-rendering`, `ue-niagara-effects`.
- **World and streaming** — `ue-world-level-streaming` (World Partition, level streaming, travel), `ue-procedural-generation` (PCG).
- **Data, save and serialization** — `ue-data-assets-tables`, `ue-serialization-savegames`.
- **Networking** — `ue-networking-replication` (replication, RPCs, relevance).
- **UI** — `ue-ui-umg-slate` (UMG, Slate, Common UI).
- **Debugging, profiling, tests** — `ue-testing-debugging`, `ue-async-threading`.
- **Editor tooling** — `ue-editor-tools` (detail customizations, menus, factories).
- **Animation** — `ue-animation-system` (AnimInstance, montages, notifies, root motion).
- **As-needed** — `ue-ai-navigation`, `ue-audio-system`, `ue-game-features`, `ue-gameplay-abilities` (GAS), `ue-gameplay-cameras`, `ue-gameplay-tags-messaging`, `ue-mass-entity`, `ue-mover`, `ue-sequencer-cinematics`, `ue-state-trees`.
## C++ skill
One router skill, `cpp` (MIT, [DiamonDinoia/skill-cpp](https://github.com/DiamonDinoia/skill-cpp) `f2617fb`), lives at `.agents/skills/cpp/` and covers general C++11 through C++23 against the C++ Core Guidelines. It detects the project language standard from the build system (**C++20** for this project, from the UBT compile flags), then loads the matching standard reference plus the topic references the task touches. Load it for any C++ work that is not Unreal-specific; the UE skills supersede it for engine API questions.
## Licensing & Distribution
- Publish project-owned code under a chosen open-source license.
+4
View File
@@ -14,6 +14,10 @@
{
"Name": "EnhancedInput",
"Enabled": true
},
{
"Name": "ChaosVehiclesPlugin",
"Enabled": true
}
]
}
@@ -65,9 +65,12 @@ AGigLineVehicle::AGigLineVehicle(const FObjectInitializer& ObjectInitializer)
{
Body->SetSkeletalMesh(MeshFinder.Object);
}
Body->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
Body->SetCollisionProfileName(TEXT("PhysicsActor"));
Body->SetSimulatePhysics(true);
// The Chaos vehicle movement component drives this mesh through its async physics; it
// calls CreateVehicle() on the movement component, which builds the wheels and the
// vehicle state. Calling SetSimulatePhysics(true) here instead put the mesh into the
// REGULAR rigid-body solver, which is why CreateVehicle() never ran (output null,
// wheels 0) and why the plain body then teleported upward in DemoMap. Leave the physics
// to the movement component.
}
if (UChaosWheeledVehicleMovementComponent* Movement =
+94
View File
@@ -680,3 +680,97 @@ A visual change needs **build → import → render**, never just a render: `ren
- The hero facade rework (split out of 0.3.0) is **closed at "surface detail that reads at 45 m"**. Done and committed: carved inscription, stepped medallions, light motto band + proud surround (all three had been buried inside the parapet), and gold JIMMY KIMMEL LIVE banner lettering.
- **Banner lettering is procedural, not ComfyUI.** `Tools/gen/generate_banner_texture.py` draws the exact title with Pillow + Liberation Sans Bold. An image model (Qwen-Image 2.1) would mangle a trademark string and is research-only, so for a _known_ string a font rasteriser is strictly better than txt2img. Reserve ComfyUI/Qwen for things that need a model, not for literal text.
- **Deferred to 0.9.0 (owner's instruction):** volute capital depth, lattice grille depth, and the full realism pass (banded rustication, measured materials, texel density, LOD/collision). The 0.9.0 "Hero façade (6840)" item records the hand-off.
## 2026-11-10 — Vehicle launch: the asset pipeline is ruled out
Re-checked the three "asset is broken" suspects with commandlets and each is **correct**:
- `vehicle_skeletal.fbx` geometry is X −2.38..2.38 m (the full car, 296 verts), not degenerate.
- Imported `SK_Vehicle_Sedan` bounds are **origin (0,0,72) extent (238,114,72) cm** — the correct 4.76 x 2.28 x 1.44 m car, so the import scale is fine.
- `Terrain_DEM` bounds are 380 x 210 m with no degenerate/NaN scale, and its body setup reports no convex/box/sphere elements.
So the "1 cm bounds" logged in the vehicle diagnostic is a **stale runtime read of the component bounds**, not an asset defect — the skeletal mesh is fine and the symptom is the teleport writing the actor transform, not a bad mesh. The launch is a runtime (Chaos movement / map) problem, not an import or build one.
## 2026-11-10 — Vehicle launch: fresh probe data, the writer is the physics sim
Rebuilt and re-ran the `ue_vehicle_probe_map.py` workflow (after `chmod u+w` the read-only `.umap`s and `rm` the four stale `VehicleProbe*` maps — `delete_asset` leaves the `.umap` on disk so the tool's own fallback never fires):
| t | control (no terrain) | terrain |
| ---- | ------------------------- | ------------------------------- |
| 0.03 | Z 300 vZ 0 | Z 313 vZ 0 |
| 1.50 | Z 306 vZ 0 | Z 442 vZ 0 |
| 2.25 | Z 299 **vZ −242** (falls) | Z 545 **vZ 1** (jumps up) |
| 3.0 | settles Z 78 | settles Z 545, then physics OFF |
- **Control falls and settles** (300 → 78 cm); **terrain rises** (313 → 545 cm) with the body velocity pinned to 0 throughout and `overlaps: 0 []`.
- **Turning physics OFF at 3 s stops the rise**: `physics OFF at Z 545` → `after 1 s without physics, Z 545`. So the writer is the physics simulation (Chaos vehicle solver teleporting the body, transform set directly), **not** an external `SetActorLocation` — the "transform written directly" reads that way because the solver moves the body without a velocity.
- **0 wheels in both runs** (control also has `setups=4 wheels=0`), so the missing wheels are not the trigger — control falls with 0 wheels too. The terrain is what flips fall → rise.
- The `1 cm` bounds (`extent 1,1,1`) persist in the diagnostic and are a **stale component read**, not the mesh (the imported `SK_Vehicle_Sedan` bounds are the correct 476 × 228 × 144 cm).
Next: the bisect plan step 2 — spawn a **stock `AWheeledVehiclePawn`** with `SK_Vehicle_Sedan` beside `AGigLineVehicle` in the terrain probe map. If the stock pawn also rises, the asset + terrain interaction is at fault; if it settles, `AGigLineVehicle`'s setup (movement subclass, mass override, panel attachment) is.
## 2026-11-10 — Vehicle "0 wheels" root cause: ChaosVehiclesPlugin was disabled
- **The Chaos vehicle never existed because `ChaosVehiclesPlugin` was not enabled.** It is `EnabledByDefault: false`, it was absent from `GigLine.uproject` (only `EnhancedInput`), and absent from `Config/*.ini`. Consequence, read straight from the engine source (`ChaosVehicleMovementComponent.cpp` `OnCreatePhysicsState`): `CreateVehicle()` runs only when `World->IsGameWorld() && GetPhysicsScene() && FChaosVehicleManager::GetVehicleManagerFromScene(PhysScene)` — and the manager is null when the plugin module never started. So the vehicle stayed at `setups=4 wheels=0 output=null`, and the mesh fell back to **regular** rigid-body physics (the constructor's `SetSimulatePhysics(true)`), which is what teleported upward.
- **Fix:** add `{"Name": "ChaosVehiclesPlugin", "Enabled": true}` to `GigLine.uproject`. Result: `CreateVehicle() entered → wheels 4`, `first UpdateState - output present`, `diag ... setups=4 wheels=4`.
- **Also removed** `SetSimulatePhysics(true)` / `SetCollisionProfileName("PhysicsActor")` / `SetCollisionEnabled` from the `AGigLineVehicle` constructor: the Chaos movement component drives the mesh through its async physics, and forcing the regular rigid-body solver is what kept the body out of the vehicle solver.
- **The plugin fix converts "teleport up" into "float at Z 545".** With 4 wheels the pawn now settles at a stable Z 545 (540 cm above the ReviewMap floor at Z 5) in *both* the control and terrain probe maps — no teleport, but the suspension holds the chassis ~5 m too high. That is the next bug: wheel radius / suspension rest-length / chassis-box offset, not the vehicle creation.
## 2026-11-10 — Vehicle: the physics-asset recreation fixed the float; box-centre +32.4 is wrong
After the plugin enablement gave 4 wheels, the pawn still floated at Z 545 in both probe
maps. Two more findings close that:
- **Regenerating the physics asset fixed the float.** The committed `PA_Vehicle_Sedan`
was a stale body (the prior "one chassis body" commit `5dd53ac` changed the builder but
the asset was never re-generated). Rebuilding it through the current
`UGigLineVehicleBuilder::build_physics_asset` (one `root` body, box 452x205x40 at
centre 0,0,−18) and assigning it back to `SK_Vehicle_Sedan.physics_asset` made the pawn
**fall and settle at Z 78** (≈ wheel radius + suspension above the floor) in both the
control and terrain probe maps. `create_vehicle` → `wheels 4`, `output present`.
- **Box centre +32.4 is wrong.** I tried moving the chassis box from −18 to
`+Extent.Z*0.45` (=+32.4) on the theory that −18 put the body below the ground. It made
it **worse**: the box then overlaps the wheel bones (at +33), the Chaos vehicle fails to
set up, and the pawn reports `simulating=0` and stays at spawn (Z 300). Reverted. The
working value is the original **−18**, because `GetWheelRestingPosition`/`LocateBoneOffset`
measures wheel position against the root **bone** (origin), not the box, so the box
centre is effectively ignored by the suspension — and +32.4 breaks the wheel/chassis
separation. Do not "correct" the box centre without re-testing settle.
- **The regeneration is not wired into any script.** `build_physics_asset` is a
`UBlueprintFunctionLibrary` (callable from Python as
`unreal.GigLineVehicleBuilder.build_physics_asset(mesh, dest, "PA_Vehicle_Sedan")`), but
`ue_vehicle_import.py` imports the skeletal mesh and never creates/assigns the physics
asset. Next step is to add that call to the import (delete + rebuild + assign + save) so
the asset is reproducible, then re-run the `-drivetest` (driving is still untested).
- **Editor vs game target:** `build_physics_asset` is `#if WITH_EDITOR`, so it lives in the
**editor** module (`libUnrealEditor-GigLine.so`). `Build.sh GigLine Linux Development`
does **not** recompile it — you must build `GigLineEditor`. The box-centre change I made
looked like a no-op because the editor module was stale; the `.so` mtime is the tell.
Uncommitted when pausing: `PA_Vehicle_Sedan.uasset` is currently at the **+32.4** box
(wrong — regenerate at −18), `SK_Vehicle_Sedan.uasset` points at it, and the four
`VehicleProbe*.umap` churn should be reverted. `GigLineVehicleBuilder.cpp` is reverted/clean.
## 2026-11-10 — Vehicle float: the delete+recreate physics asset is fragile
The "settle at Z 78" result was transient. Recreating `PA_Vehicle_Sedan` again (the
second/third delete + rebuild + assign cycle) flipped the pawn to **`simulating=0` and
stuck at spawn Z 300**, with the same box centre −18 that had settled minutes earlier.
So the box centre is not what flips settle↔stuck; the **delete + recreate cycle is** —
the same failure shape as the material `delete_asset` bug earlier: `delete_asset` does not
fully release the package, `NewObject`/`CreatePackage` then reuses stale state, and the
rebuilt body ends up not simulating (`PhysicsType` left at default, not `Simulated`).
- The plugin enablement (`5e328f5`) remains the solid fix: `CreateVehicle` → 4 wheels.
- The physics asset still needs a **proper** fix, not delete+recreate: set
`BodySetup->PhysicsType = EPhysicsType::PhysType_Simulated` (and any Chaos-required
flags) in `GigLineVehicleBuilder::BuildPhysicsAsset`, and update the asset **in place**
rather than deleting it.
- `ue_vehicle_import.py` was wired to regenerate the asset, but its `SOURCE` is stale
(`/tmp/gigline/hatch` — the static template) while the skeletal FBX lives in
`/tmp/gigline/vehicle`; the wired block is therefore never reached. Fix the source path
(or move the FBX) before relying on the import to rebuild the physics asset.
- Driving (`-drivetest`) is still untested; settling worked only in the transient run.
+4
View File
@@ -11,6 +11,8 @@ Actionable task list for this project. This file is the single, central place fo
## Toolchain — end-to-end asset check (done 2026-11-10)
> [!IMPORTANT] Plan for 0.3.1 before other patches
The chain that answers "does the asset look right in both the staging map and the game map" is now one command: `python3 Tools/review/e2e_asset_check.py`. It renders the Blender source, empties both maps, runs both builders, captures, judges with two models, and regenerates `Screenshots/README.md`. Last full run: **PASS on all six steps** (empty maps 9 s, demo build 10 s, review build 9 s, captures 80 s, judge 388 s).
- [x] Group the sixty tools by sandbox: `Tools/unreal/`, `Tools/blender/`, `Tools/review/`, `Tools/gen/`, `Tools/input/`, with the layout documented in `Tools/README.md`.
@@ -133,6 +135,8 @@ Deferred rather than guessed. What a fresh session needs:
Two facts that matter more than the hypothesis list above: on `ReviewMap`'s flat floor the **same pawn falls correctly** (Z 200 -> 87, vZ -663), and in `DemoMap` it teleports upward at any height - including 25 m in the air - with the transform written directly (clamping velocity to zero every frame does not stop it) while the body reports a near-zero velocity. Disabling the movement component's tick changes nothing; there are zero overlaps; the body is genuinely dynamic (`profile=PhysicsActor`); `shadow`-style collision explanations were ruled out by staging the terrain with **collision disabled entirely** and seeing the pawn still rise.
**Fresh probe data (2026-11-10) — the writer is the physics sim, not an external setter.** Control (no terrain) falls and settles (300 → 78 cm); terrain rises (313 → 545 cm) with body velocity pinned to 0 and `overlaps: 0`. **Turning physics OFF at 3 s stops the rise** (`physics OFF at Z 545` → `after 1 s without physics, Z 545`), so the Chaos vehicle solver is teleporting the body (transform set directly, no velocity). Both runs have `setups=4 wheels=0`, so the missing wheels are not the trigger — control falls with 0 wheels too; the terrain is what flips fall → rise. The asset pipeline is ruled out (FBX, `SK_Vehicle_Sedan` import 476×228×144 cm, physics asset 452×205×40, `Terrain_DEM` 380×210 m all correct; the `1 cm` bounds is a stale component read). Next: bisect step 2 — a stock `AWheeledVehiclePawn` beside `AGigLineVehicle` in the terrain probe map.
### Tasks
- [D] Give `SK_Vehicle_Sedan` a physics asset whose single chassis body matches the car's box and is positioned below the shell, then confirm the pawn settles on the road instead of launching. **Deferred**: the chassis box, mass and gravity all measure correct, so this is not the shape of the body.
+38 -38
View File
@@ -34,45 +34,14 @@ run_cmdlet() {
# Order matters. The demo builder rebuilds the site materials, so any actor staged
# by another tool must be (re)staged after it or it keeps a reference to a material
# that no longer exists - that is how 604 actors ended up untextured once.
run_cmdlet ue_build_demo_map.py "1/5 rebuild the demo map (materials, site, frontage)"
run_cmdlet ue_apply_facade_kit.py "2/5 assemble the facade kit"
run_cmdlet ue_stage_theatre_trim.py "3/5 stage the theatre trim"
run_cmdlet ue_audit_scene.py "4/6 audit the scene (materials, placement, labels)"
run_cmdlet ue_build_review_map.py "5/6 stage the demo shot cameras"
run_cmdlet ue_build_demo_map.py "1/7 rebuild the demo map (materials, site, frontage)"
run_cmdlet ue_apply_facade_kit.py "2/7 assemble the facade kit"
run_cmdlet ue_stage_theatre_trim.py "3/7 stage the theatre trim"
run_cmdlet ue_audit_scene.py "4/7 audit the scene (materials, placement, labels)"
run_cmdlet ue_build_review_map.py "5/7 stage the demo shot cameras"
tail -3 /tmp/ue-review-map-result.txt 2>/dev/null || true
echo "== 6/6 capture the demo map =="
# Clear the engine's screenshot folder first. The collect step below copies every
# review-*.png it finds in Saved/, so without this a shot retired from the camera list
# keeps coming back as a stale file - that is how a removed walk shot reappeared with
# byte-identical content after a run that never captured it.
rm -rf "$REPO"/Game/GigLine/Saved/Screenshots/* "$HOME"/.config/Epic/GigLine/Saved/Screenshots/* 2>/dev/null || true
# Recreate the folder: the engine writes into it and does not recreate a deleted
# directory, which silently produced zero captures in the run that removed it.
mkdir -p "$REPO/Game/GigLine/Saved/Screenshots/LinuxEditor"
# The engine path is absolute: this step runs from the repo, not from $ENGINE, and a
# bare ./Engine/... failed with "No such file or directory" while every commandlet
# above succeeded through run_cmdlet's cd.
"$ENGINE/Engine/Binaries/Linux/UnrealEditor-Cmd" "$PROJ" /Game/Hollywood/DemoMap \
-game -windowed -ResX=1920 -ResY=1080 -nopause -nosplash -thirdperson -stdout
echo "== collect the demo screenshots =="
mkdir -p "$REPO/Screenshots/demo"
rm -f "$REPO"/Screenshots/demo/review-*.png
found=0
for src in \
"$REPO/Game/GigLine/Saved/Screenshots"/*/review-*.png \
"$REPO/Game/GigLine/Saved/Screenshots"/*/*/review-*.png \
"$HOME/.config/Epic/GigLine/Saved/Screenshots"/*/review-*.png; do
[ -e "$src" ] || continue
case "$(basename "$src")" in
*demo-*) cp -f "$src" "$REPO/Screenshots/demo/"; found=1 ;;
esac
done
[ "$found" = "1" ] || { echo "ERROR: no demo screenshots found"; exit 1; }
ls -1 "$REPO/Screenshots/demo"
echo "== 6b/7 capture the review map (asset staging) =="
echo "== 6a/7 capture the review map (asset staging) =="
# The ReviewMap stages the models on their own - imported assets, camera angles and the
# lighting comparison - which is a different question from "does the game map look right".
# Its shots collect into Screenshots/review/ so the two reviews stay separate.
@@ -81,7 +50,7 @@ mkdir -p "$REPO/Game/GigLine/Saved/Screenshots/LinuxEditor"
"$ENGINE/Engine/Binaries/Linux/UnrealEditor-Cmd" "$PROJ" /Game/Test/ReviewMap \
-game -windowed -ResX=1920 -ResY=1080 -nopause -nosplash -stdout
echo "== 6c/7 collect the review-map screenshots =="
echo "== 6b/7 collect the review-map screenshots =="
mkdir -p "$REPO/Screenshots/review"
rm -f "$REPO"/Screenshots/review/review-*.png
found_review=0
@@ -98,6 +67,37 @@ done
[ "$found_review" = "1" ] || echo "WARN: no review-map screenshots found"
ls -1 "$REPO/Screenshots/review" 2>/dev/null | head -20
echo "== 6c/7 capture the demo map =="
# Clear the engine's screenshot folder first. The collect step below copies every
# review-*.png it finds in Saved/, so without this a shot retired from the camera list
# keeps coming back as a stale file - that is how a removed walk shot reappeared with
# byte-identical content after a run that never captured it.
rm -rf "$REPO"/Game/GigLine/Saved/Screenshots/* "$HOME"/.config/Epic/GigLine/Saved/Screenshots/* 2>/dev/null || true
# Recreate the folder: the engine writes into it and does not recreate a deleted
# directory, which silently produced zero captures in the run that removed it.
mkdir -p "$REPO/Game/GigLine/Saved/Screenshots/LinuxEditor"
# The engine path is absolute: this step runs from the repo, not from $ENGINE, and a
# bare ./Engine/... failed with "No such file or directory" while every commandlet
# above succeeded through run_cmdlet's cd.
"$ENGINE/Engine/Binaries/Linux/UnrealEditor-Cmd" "$PROJ" /Game/Hollywood/DemoMap \
-game -windowed -ResX=1920 -ResY=1080 -nopause -nosplash -thirdperson -stdout
echo "== 6d/7 collect the demo screenshots =="
mkdir -p "$REPO/Screenshots/demo"
rm -f "$REPO"/Screenshots/demo/review-*.png
found=0
for src in \
"$REPO/Game/GigLine/Saved/Screenshots"/*/review-*.png \
"$REPO/Game/GigLine/Saved/Screenshots"/*/*/review-*.png \
"$HOME/.config/Epic/GigLine/Saved/Screenshots"/*/review-*.png; do
[ -e "$src" ] || continue
case "$(basename "$src")" in
*demo-*) cp -f "$src" "$REPO/Screenshots/demo/"; found=1 ;;
esac
done
[ "$found" = "1" ] || { echo "ERROR: no demo screenshots found"; exit 1; }
ls -1 "$REPO/Screenshots/demo"
# The scene audit is a gate: a wrong-material or misplaced actor is exactly the
# kind of failure that still renders plausibly.