feat(hero): facade rework — inscription, medallions, motto band, banner lettering #39

Merged
zwitschi merged 93 commits from hero-facade-0.3.0 into main 2026-11-10 15:37:40 +01:00
54 changed files with 254 additions and 112 deletions
Showing only changes of commit 72d5bab427 - Show all commits

No files matched your search

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+99 -57
View File
@@ -1,36 +1,111 @@
# Screenshots — demo-map visual review
# Screenshots
Rendered captures of **the demo map only** — what a player sees when the demo runs. Generated by [`Tools/render_screenshots.sh`](../Tools/render_screenshots.sh); the PNGs are committed so a reviewer sees exactly what the build rendered.
Two reviews in one file:
The review staging map (`/Game/Test/ReviewMap`) is not part of this set: it exists to hold the camera list and the hero detail rig, and it is not what ships.
- **ReviewMap** stages imported assets on their own — individual objects, the Blender import, the camera angles and the lighting comparison. Nothing from the game map is involved.
- **DemoMap** is the actual game map as it is being developed: the street, the buildings, the player and the vehicle in their real placement.
## How to regenerate
Both capture passes are produced by [`Tools/render_screenshots.sh`](../Tools/render_screenshots.sh); the asset renders come from [`Tools/blender_screenshots.py`](../Tools/blender_screenshots.py).
```bash
Tools/render_screenshots.sh --verify
```
## Status
Stages the demo shot cameras, runs the game on `/Game/Hollywood/DemoMap`, copies the PNGs into `Screenshots/demo/` and then runs [`Tools/visual_loop.py`](../Tools/visual_loop.py) `--verify` to check that each shot still shows the content its expectation describes. About a minute.
**This is the only place approval state and issue notes are recorded.** Every image requires a fresh approval because every image changed.
## Status at a glance
| # | Part | Item | Status | Note |
| --- | --- | --- | --- | --- |
| 1 | ReviewMap | [character feet](review/review-character-feet.png) | ☐ pending | |
| 2 | ReviewMap | [character](review/review-character.png) | ☐ pending | |
| 3 | ReviewMap | [ground contact](review/review-ground-contact.png) | ☐ pending | |
| 4 | ReviewMap | [hero facade front](review/review-hero-facade-front.png) | ☐ pending | |
| 5 | ReviewMap | [hero facade oblique](review/review-hero-facade-oblique.png) | ☐ pending | |
| 6 | ReviewMap | [hero portals](review/review-hero-portals.png) | ☐ pending | |
| 7 | ReviewMap | [npc prefab](review/review-npc-prefab.png) | ☐ pending | |
| 8 | ReviewMap | [overview](review/review-overview.png) | ☐ pending | |
| 9 | ReviewMap | [player placement](review/review-player-placement.png) | ☐ pending | |
| 10 | ReviewMap | [scale cubes](review/review-scale-cubes.png) | ☐ pending | |
| 11 | ReviewMap | [shadow compare](review/review-shadow-compare.png) | ☐ pending | |
| 12 | ReviewMap | [vehicle](review/review-vehicle.png) | ☐ pending | |
| 13 | DemoMap | [crossing](demo/review-demo-crossing.png) | ☐ pending | |
| 14 | DemoMap | [feet](demo/review-demo-feet.png) | ☐ pending | |
| 15 | DemoMap | [north buildings](demo/review-demo-north-buildings.png) | ☐ pending | |
| 16 | DemoMap | [retail frontage](demo/review-demo-retail-frontage.png) | ☐ pending | |
| 17 | DemoMap | [spawn view](demo/review-demo-spawn-view.png) | ☐ pending | character and car float |
| 18 | DemoMap | [street level](demo/review-demo-street-level.png) | ☐ pending | character and car float |
| 19 | DemoMap | [third person front](demo/review-demo-third-person-front.png) | ☐ pending | character and car float |
| 20 | DemoMap | [third person walk](demo/review-demo-third-person-walk.png) | ☐ pending | character and car float |
| 21 | DemoMap | [third person](demo/review-demo-third-person.png) | ☐ pending | character and car float |
| 22 | DemoMap | [vehicle](demo/review-demo-vehicle.png) | ☐ pending | |
| 23 | DemoMap | [wheel](demo/review-demo-wheel.png) | ☐ pending | |
**This is the only place approval state is recorded.** Every image below requires a fresh approval because all of them changed; a note in this table is the record, and the per-shot sections describe what a shot should show rather than whether it passed.
**Status:** ☐ pending · ☑ approved · ☒ rejected. "ReviewMap" rows are asset staging; "DemoMap" rows are the game map.
| # | Shot | Status | Note |
| --- | --- | --- | --- |
| 1 | [spawn-view](#1-spawn-view) | ☐ pending | character and car float |
| 2 | [street-level](#2-street-level) | ☐ pending | character and car float |
| 3 | [crossing](#3-crossing) | ☐ pending | |
| 4 | [north-buildings](#4-north-buildings) | ☐ pending | |
| 5 | [retail-frontage](#5-retail-frontage) | ☐ pending | |
| 6 | [third-person](#6-third-person) | ☐ pending | character and car float |
| 7 | [third-person-front](#7-third-person-front) | ☐ pending | character and car float |
| 8 | [third-person-walk](#8-third-person-walk) | ☐ pending | character and car float |
| 9 | [vehicle](#9-vehicle) | ☐ pending | |
| 10 | [feet](#10-feet) | ☐ pending | |
| 11 | [wheel](#11-wheel) | ☐ pending | |
## Blender vs UE
**Status:** ☐ pending · ☑ approved · ☒ rejected.
<!-- blender-comparison:begin -->
### Blender vs in-game
Regenerated by `Tools/build_comparison_md.py`; everything between the markers is rewritten, so edit outside them.
- Captures: `Screenshots/review/` and `Screenshots/demo/` (23 files)
- Blender renders: `Screenshots/blender/` (22 files), made by `Tools/blender_screenshots.py`
Left is what the engine draws, right is what Blender has. A gap between them is the import problem, not a taste problem: scale, material colour, missing parts and split systems all show up here before they show up in a review.
### Side by side
| In-game | Blender | Shot |
| --- | --- | --- |
| <img src="review/review-character-feet.png" width="520"> | <img src="blender/character-angle-035.png" width="520"> | `review-character-feet.png` |
| <img src="review/review-character.png" width="520"> | <img src="blender/character-angle-035.png" width="520"> | `review-character.png` |
| <img src="review/review-ground-contact.png" width="520"> | — | `review-ground-contact.png` |
| <img src="review/review-hero-facade-front.png" width="520"> | — | `review-hero-facade-front.png` |
| <img src="review/review-hero-facade-oblique.png" width="520"> | — | `review-hero-facade-oblique.png` |
| <img src="review/review-hero-portals.png" width="520"> | — | `review-hero-portals.png` |
| <img src="review/review-npc-prefab.png" width="520"> | — | `review-npc-prefab.png` |
| <img src="review/review-overview.png" width="520"> | — | `review-overview.png` |
| <img src="review/review-player-placement.png" width="520"> | — | `review-player-placement.png` |
| <img src="review/review-scale-cubes.png" width="520"> | — | `review-scale-cubes.png` |
| <img src="review/review-shadow-compare.png" width="520"> | — | `review-shadow-compare.png` |
| <img src="review/review-vehicle.png" width="520"> | <img src="blender/vehicle-angle-035.png" width="520"> | `review-vehicle.png` |
| <img src="demo/review-demo-crossing.png" width="520"> | — | `review-demo-crossing.png` |
| <img src="demo/review-demo-feet.png" width="520"> | — | `review-demo-feet.png` |
| <img src="demo/review-demo-north-buildings.png" width="520"> | — | `review-demo-north-buildings.png` |
| <img src="demo/review-demo-retail-frontage.png" width="520"> | — | `review-demo-retail-frontage.png` |
| <img src="demo/review-demo-spawn-view.png" width="520"> | — | `review-demo-spawn-view.png` |
| <img src="demo/review-demo-street-level.png" width="520"> | — | `review-demo-street-level.png` |
| <img src="demo/review-demo-third-person-front.png" width="520"> | <img src="blender/character-angle-035.png" width="520"> | `review-demo-third-person-front.png` |
| <img src="demo/review-demo-third-person-walk.png" width="520"> | <img src="blender/character-angle-035.png" width="520"> | `review-demo-third-person-walk.png` |
| <img src="demo/review-demo-third-person.png" width="520"> | <img src="blender/character-angle-035.png" width="520"> | `review-demo-third-person.png` |
| <img src="demo/review-demo-vehicle.png" width="520"> | <img src="blender/vehicle-angle-035.png" width="520"> | `review-demo-vehicle.png` |
| <img src="demo/review-demo-wheel.png" width="520"> | — | `review-demo-wheel.png` |
7 of 23 in-game captures have a Blender counterpart; the rest are street and building shots whose meshes come from the Geo pipeline rather than a .blend.
Approval and issue notes are recorded only in the status table at the top of this file.
<!-- blender-comparison:end -->
## ReviewMap — asset staging
The staging map (`/Game/Test/ReviewMap`, built by [`Tools/ue_build_review_map.py`](../Tools/ue_build_review_map.py)) always stages the assets the latest Blender import produced: the character with its hair, clothing and materials, the vehicle's five systems, the scale cubes, the NPC prefab and the hero facade.
| Shot | Shows |
| --- | --- |
| ![](review/review-character-feet.png) | `review-character-feet.png` |
| ![](review/review-character.png) | `review-character.png` |
| ![](review/review-ground-contact.png) | `review-ground-contact.png` |
| ![](review/review-hero-facade-front.png) | `review-hero-facade-front.png` |
| ![](review/review-hero-facade-oblique.png) | `review-hero-facade-oblique.png` |
| ![](review/review-hero-portals.png) | `review-hero-portals.png` |
| ![](review/review-npc-prefab.png) | `review-npc-prefab.png` |
| ![](review/review-overview.png) | `review-overview.png` |
| ![](review/review-player-placement.png) | `review-player-placement.png` |
| ![](review/review-scale-cubes.png) | `review-scale-cubes.png` |
| ![](review/review-shadow-compare.png) | `review-shadow-compare.png` |
| ![](review/review-vehicle.png) | `review-vehicle.png` |
## DemoMap — in-game
## 1. Spawn view
@@ -212,36 +287,3 @@ Resolved and removed from this list on 2026-11-09:
- "The El Capitan Theatre has no marquee or blade sign" — `Tools/ue_stage_theatre_trim.py` stages all six trim actors; verified in `/tmp/gigline/theatre-import.txt`.
- "Materials are not yet created in Blender or fully integrated" — contradicted by the scene conventions above: every surface carries a look-dev material built in `Tools/ue_build_demo_map.py`.
<!-- blender-comparison:begin -->
## Blender vs in-game
Regenerated by `Tools/build_comparison_md.py`; everything between the markers is rewritten, so edit outside them.
- In-game captures: `Screenshots/demo/` (11 files)
- Blender renders: `Screenshots/blender/` (22 files), made by `Tools/blender_screenshots.py`
Left is what the engine draws, right is what Blender has. A gap between them is the import problem, not a taste problem: scale, material colour, missing parts and split systems all show up here before they show up in a review.
### Side by side
| In-game | Blender | Shot |
| --- | --- | --- |
| <img src="demo/review-demo-crossing.png" width="520"> | — | `review-demo-crossing.png` |
| <img src="demo/review-demo-feet.png" width="520"> | — | `review-demo-feet.png` |
| <img src="demo/review-demo-north-buildings.png" width="520"> | — | `review-demo-north-buildings.png` |
| <img src="demo/review-demo-retail-frontage.png" width="520"> | — | `review-demo-retail-frontage.png` |
| <img src="demo/review-demo-spawn-view.png" width="520"> | — | `review-demo-spawn-view.png` |
| <img src="demo/review-demo-street-level.png" width="520"> | — | `review-demo-street-level.png` |
| <img src="demo/review-demo-third-person-front.png" width="520"> | <img src="blender/character-angle-035.png" width="520"> | `review-demo-third-person-front.png` |
| <img src="demo/review-demo-third-person-walk.png" width="520"> | <img src="blender/character-angle-035.png" width="520"> | `review-demo-third-person-walk.png` |
| <img src="demo/review-demo-third-person.png" width="520"> | <img src="blender/character-angle-035.png" width="520"> | `review-demo-third-person.png` |
| <img src="demo/review-demo-vehicle.png" width="520"> | <img src="blender/vehicle-angle-035.png" width="520"> | `review-demo-vehicle.png` |
| <img src="demo/review-demo-wheel.png" width="520"> | — | `review-demo-wheel.png` |
4 of 11 in-game captures have a Blender counterpart; the rest are street and building shots whose meshes come from the Geo pipeline rather than a .blend.
Approval and issue notes are recorded only in the status table at the top of this file.
<!-- blender-comparison:end -->
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+11 -4
View File
@@ -23,6 +23,7 @@ import shutil
import subprocess
DEMO_DIR = "Screenshots/demo"
REVIEW_DIR = "Screenshots/review"
BLENDER_DIR = "Screenshots/blender"
OUT = "Screenshots/README.md"
BEGIN = "<!-- blender-comparison:begin -->"
@@ -43,6 +44,12 @@ SHOT_ALIASES = {
}
def review_shots():
if not os.path.isdir(REVIEW_DIR):
return []
return sorted(f for f in os.listdir(REVIEW_DIR) if f.endswith(".png"))
def demo_shots():
if not os.path.isdir(DEMO_DIR):
return []
@@ -95,15 +102,15 @@ def image_cell(path, width=520):
def build():
shots = demo_shots()
shots = review_shots() + demo_shots()
renders = blender_renders()
lines = [
"## Blender vs in-game",
"### Blender vs in-game",
"",
"Regenerated by `Tools/build_comparison_md.py`; everything between the markers is",
"rewritten, so edit outside them.",
"",
f"- In-game captures: `{DEMO_DIR}/` ({len(shots)} files)",
f"- Captures: `{REVIEW_DIR}/` and `{DEMO_DIR}/` ({len(shots)} files)",
f"- Blender renders: `{BLENDER_DIR}/` ({len(renders)} files), made by `Tools/blender_screenshots.py`",
"",
"Left is what the engine draws, right is what Blender has. A gap between them is the",
@@ -121,7 +128,7 @@ def build():
match = best_blender_match(shot, renders)
paired[shot] = match
lines.append(
f"| {image_cell(f'{DEMO_DIR}/{shot}')} "
f"| {image_cell(f'{REVIEW_DIR if shot in review_shots() else DEMO_DIR}/{shot}')} "
f"| {image_cell(f'{BLENDER_DIR}/{match}' if match else None)} "
f"| `{shot}` |")
if not shots:
+27
View File
@@ -69,6 +69,33 @@ 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) =="
# 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.
rm -rf "$REPO"/Game/GigLine/Saved/Screenshots/* 2>/dev/null || true
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 =="
mkdir -p "$REPO/Screenshots/review"
rm -f "$REPO"/Screenshots/review/review-*.png
found_review=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/review/"; found_review=1 ;;
esac
done
[ "$found_review" = "1" ] || echo "WARN: no review-map screenshots found"
ls -1 "$REPO/Screenshots/review" 2>/dev/null | head -20
# The scene audit is a gate: a wrong-material or misplaced actor is exactly the
# kind of failure that still renders plausibly.
if grep -q "failure(s) ==" /tmp/gigline/scene-audit.txt 2>/dev/null; then
+30
View File
@@ -192,6 +192,32 @@ try:
else:
log("MISSING: /Script/GigLine.GigLineCharacter")
# --- vehicle, from the latest Blender import ------------------------------
# Staged from the same assets the demo map uses, so the review always shows what
# Tools/vehicle_from_template.py most recently produced rather than a copy that can
# drift. The panels are plain static meshes because the vehicle pawn keeps its
# physics only.
vehicle_origin = unreal.Vector(-1600.0, 600.0, 0.0)
vehicle_systems = ("SM_Vehicle_Body", "SM_Vehicle_Trim", "SM_Vehicle_Glass",
"SM_Vehicle_Lights", "SM_Vehicle_Wheels")
staged_vehicle = 0
for index, mesh_name in enumerate(vehicle_systems):
mesh = unreal.EditorAssetLibrary.load_asset(f"/Game/Hollywood/Vehicle/{mesh_name}")
if not mesh:
log(f"MISSING vehicle system: {mesh_name}")
continue
piece = spawn(unreal.StaticMeshActor, vehicle_origin, f"Review_Vehicle_{index:02d}")
if piece:
piece.static_mesh_component.set_static_mesh(mesh)
piece.static_mesh_component.set_mobility(unreal.ComponentMobility.STATIC)
if mesh_name == "SM_Vehicle_Body":
paint = unreal.EditorAssetLibrary.load_asset(
"/Game/Hollywood/Vehicle/M_Vehicle_HatchPaint")
if paint:
piece.static_mesh_component.set_material(0, paint)
staged_vehicle += 1
log(f"staged: {staged_vehicle} vehicle systems at the staging area")
# --- NPC prefab -----------------------------------------------------------
# The reusable NPC person: a collision capsule + visible MPFB body, standing
# feet on Z 0, placed clear of the pawn so the prefab reads on its own.
@@ -211,6 +237,10 @@ try:
cams = [
# wide scene: character, both cubes, floor
("overview", unreal.Vector(700, -900, 620), unreal.Vector(0, 300, 80)),
# the vehicle staging, three-quarter front, same framing idea as the demo shot
("vehicle", unreal.Vector(-900, -200, 260), unreal.Vector(-1600, 600, 90)),
# ground-level contact check: feet on the floor, shadow at the contact point
("character-feet", unreal.Vector(220, -420, 45), unreal.Vector(0, 0, 25)),
# the MPFB mannequin, mid shot
("character", unreal.Vector(200, -300, 160), unreal.Vector(0, 0, 85)),
# both cubes from the south: contact shadow vs cast shadow