From 830ce596fa7a8710286185388a951d984152113c Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Sat, 7 Nov 2026 17:56:21 +0100 Subject: [PATCH] lighting: fixed daytime rig, real surface materials, crossing fix Fixed daytime lighting and basic ambience for the DemoMap: - Sun pitch -50 / yaw 120 (intensity 4, warm), sky light 2.6 with real-time capture, sky atmosphere, height fog, and an unbound post-process volume with fixed exposure (min = max brightness) so no shot washes out or crushes to black depending on how much sky it frames. - Root cause of the washed-out look was NOT the sun: Blender's flat diffuse colours do not survive the FBX->Unreal import. Every site material arrived white and the terrain arrived with WorldGridMaterial. The demo builder now creates and assigns M_Asphalt / M_Concrete / M_Ground / M_RoadPaint. - Crossing fix: each zebra band was its own object, so Blender renamed the duplicates .001/.002 and site assembly (exact-name append) kept only one. All bands are now a single Crossing_Markings mesh (36 verts, 3 bands). - Terrain gets a Ground_Earth material in the builder so it stops importing as a white grid. - Demo cameras: street-level no longer sits inside the facade body (was rendering its back face), and the crossing camera aims at the designated s=130.1 crossing (UE X -14956) looking along the crosswalk. Screenshots re-rendered; the four demo views are marked needs-review for the owner's approval. MEMORY.md records the material-import, exposure, light_color, band-naming and camera-bounds traps. --- Art/blender/site/site-2026-11-06.blend | 4 +- Art/blender/street/street-2026-11-06.blend | 4 +- Art/blender/street/street_mesh.py | 42 +++--- Art/blender/terrain/terrain-2026-11-06.blend | 4 +- Art/blender/terrain/terrain_from_dem.py | 6 + Game/GigLine/Content/Hollywood/DemoMap.umap | 4 +- .../Hollywood/Site/Crossing_Markings.uasset | 4 +- .../Content/Hollywood/Site/Kerb_North.uasset | 2 +- .../Content/Hollywood/Site/Kerb_South.uasset | 2 +- .../Content/Hollywood/Site/M_Asphalt.uasset | 3 + .../Content/Hollywood/Site/M_Concrete.uasset | 3 + .../Content/Hollywood/Site/M_Ground.uasset | 3 + .../Content/Hollywood/Site/M_RoadPaint.uasset | 3 + .../Hollywood/Site/Road_Hollywood.uasset | 2 +- .../Hollywood/Site/Sidewalk_North.uasset | 2 +- .../Hollywood/Site/Sidewalk_South.uasset | 2 +- .../Content/Hollywood/Site/Terrain_DEM.uasset | 4 +- Game/GigLine/Content/Test/ReviewMap.umap | 2 +- MEMORY.md | 9 ++ Screenshots/README.md | 18 ++- Screenshots/demo/review-demo-crossing.png | 4 +- .../demo/review-demo-north-buildings.png | 4 +- Screenshots/demo/review-demo-spawn-view.png | 4 +- Screenshots/demo/review-demo-street-level.png | 4 +- Screenshots/review-character.png | 4 +- Screenshots/review-ground-contact.png | 4 +- Screenshots/review-hero-facade-front.png | 4 +- Screenshots/review-hero-facade-oblique.png | 4 +- Screenshots/review-hero-portals.png | 4 +- Screenshots/review-npc-prefab.png | 4 +- Screenshots/review-overview.png | 4 +- Screenshots/review-player-placement.png | 4 +- Screenshots/review-scale-cubes.png | 4 +- Screenshots/review-shadow-compare.png | 4 +- TODO.md | 2 +- Tools/ue_build_demo_map.py | 129 ++++++++++++++++-- Tools/ue_build_review_map.py | 13 +- 37 files changed, 241 insertions(+), 82 deletions(-) create mode 100644 Game/GigLine/Content/Hollywood/Site/M_Asphalt.uasset create mode 100644 Game/GigLine/Content/Hollywood/Site/M_Concrete.uasset create mode 100644 Game/GigLine/Content/Hollywood/Site/M_Ground.uasset create mode 100644 Game/GigLine/Content/Hollywood/Site/M_RoadPaint.uasset diff --git a/Art/blender/site/site-2026-11-06.blend b/Art/blender/site/site-2026-11-06.blend index 83aa233..cb34036 100644 --- a/Art/blender/site/site-2026-11-06.blend +++ b/Art/blender/site/site-2026-11-06.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d6d303913de1a2c24b3e04ceb6c43a84a46355470b14ebab6acf146f7dbb636 -size 991800 +oid sha256:2a54ae747e4990399c8f269b8ebe5a3f1bf0d6cca4ac2dd9ca2562f66f5eedfb +size 994709 diff --git a/Art/blender/street/street-2026-11-06.blend b/Art/blender/street/street-2026-11-06.blend index 71028dc..ae518a9 100644 --- a/Art/blender/street/street-2026-11-06.blend +++ b/Art/blender/street/street-2026-11-06.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6f546fcf86cd08a5e37571a7f42b2e6615925f71f7ca5ed850e801313591f70 -size 514043 +oid sha256:5744a2138e894a485f026d92b9311f7ecde12c280dd096bbbde5776694b32ae2 +size 514185 diff --git a/Art/blender/street/street_mesh.py b/Art/blender/street/street_mesh.py index 201b616..368a18c 100644 --- a/Art/blender/street/street_mesh.py +++ b/Art/blender/street/street_mesh.py @@ -261,20 +261,24 @@ def main(): paint = bpy.data.materials.new("Road_Paint_White") paint.diffuse_color = (0.85, 0.85, 0.85, 1.0) - def crossing_band(s_c): + # All three bands live in ONE object. Creating one object per crossing used + # to produce Crossing_Markings, .001 and .002; site assembly appends by the + # exact name, so only the first crossing ever reached the game. + band_verts, band_faces = [], [] + + def add_crossing_band(s_c): # Stripes are emitted as two triangles each: the road crown makes the # quad non-planar and the FBX SDK refuses to triangulate it on import # ("unable to triangulate mesh" — the mesh is omitted entirely). zone = 3.0 # band length along the road, m w, g = 0.5, 0.5 # stripe width / gap, m span = (SOUTH_GUTTER + 0.2, NORTH_GUTTER - 0.2) - verts, faces = [], [] n = int(zone // (w + g)) covered = n * w + (n - 1) * g # actual stripe coverage, centred on s_c for k in range(n): s0 = s_c - covered / 2 + k * (w + g) - corners = [] - base_idx = len(verts) # verts grows only after the stripe is sampled + base_idx = len(band_verts) # sampled positions, then vertices + pts = [] for s_edge in (s0, s0 + w): base, normal, _ = at(s_edge) for off in span: @@ -284,23 +288,23 @@ def main(): # interpolates the DEM along 1 m rows while the markings sample # only their own corners, so a 1 cm lift left parts of the # stripes sunk under the road and invisible in game. - corners.append((base_idx + len(corners), (x, y, (dem_z(x, y) or 0.0) + 0.06))) - # corners order: (s0,off_s), (s0,off_n), (s1,off_s), (s1,off_n) - idx = [c[0] for c in corners] - verts.extend(c[1] for c in corners) - faces.append((idx[0], idx[3], idx[1])) - faces.append((idx[0], idx[2], idx[3])) - mesh = bpy.data.meshes.new("Crossing_Markings") - mesh.from_pydata(verts, [], faces) - mesh.update() - mesh.materials.append(paint) - obj = bpy.data.objects.new("Crossing_Markings", mesh) - bpy.context.scene.collection.objects.link(obj) - return obj + pts.append((x, y, (dem_z(x, y) or 0.0) + 0.06)) + # pts order: (s0,off_s), (s0,off_n), (s1,off_s), (s1,off_n) + band_verts.extend(pts) + band_faces.append((base_idx + 0, base_idx + 3, base_idx + 1)) + band_faces.append((base_idx + 0, base_idx + 2, base_idx + 3)) for s_c in CROSSINGS: - band = crossing_band(s_c) - print(f"crossing band at s={s_c:.1f}: {len(band.data.polygons)} stripes") + add_crossing_band(s_c) + + mesh = bpy.data.meshes.new("Crossing_Markings") + mesh.from_pydata(band_verts, [], band_faces) + mesh.update() + mesh.materials.append(paint) + markings = bpy.data.objects.new("Crossing_Markings", mesh) + bpy.context.scene.collection.objects.link(markings) + print(f"crossing markings: {len(CROSSINGS)} bands, " + f"{len(markings.data.vertices)} verts, {len(markings.data.polygons)} faces") for obj in (road, sw_s, sw_n): print(f"{obj.name:16} verts {len(obj.data.vertices):5} faces {len(obj.data.polygons):5}") diff --git a/Art/blender/terrain/terrain-2026-11-06.blend b/Art/blender/terrain/terrain-2026-11-06.blend index c294b9b..fd74a93 100644 --- a/Art/blender/terrain/terrain-2026-11-06.blend +++ b/Art/blender/terrain/terrain-2026-11-06.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9707f1256667ce156f8104cf84c461a1ee074a2a62c932fd377c9bb39e8f41d8 -size 431130 +oid sha256:bf33bfa11b78194c3dce1f061aaf904af4d82d7294f291772073e8d7d31ac016 +size 433475 diff --git a/Art/blender/terrain/terrain_from_dem.py b/Art/blender/terrain/terrain_from_dem.py index aa007cf..1ecfde7 100644 --- a/Art/blender/terrain/terrain_from_dem.py +++ b/Art/blender/terrain/terrain_from_dem.py @@ -224,6 +224,12 @@ def main(): mesh = bpy.data.meshes.new("Terrain_DEM") mesh.from_pydata(verts, [], faces) mesh.update() + # Without a material the mesh imports into Unreal with the default white + # material and blows out under the demo sun. A muted earth tone keeps the + # ground reading as ground. + ground = bpy.data.materials.new("Ground_Earth") + ground.diffuse_color = (0.30, 0.28, 0.25, 1.0) + mesh.materials.append(ground) obj = bpy.data.objects.new("Terrain_DEM", mesh) bpy.context.scene.collection.objects.link(obj) diff --git a/Game/GigLine/Content/Hollywood/DemoMap.umap b/Game/GigLine/Content/Hollywood/DemoMap.umap index 97dc8de..adfeb64 100644 --- a/Game/GigLine/Content/Hollywood/DemoMap.umap +++ b/Game/GigLine/Content/Hollywood/DemoMap.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a2b7a61aa8b3c86c3944849107aa00d3ab6fa0d35056d50b02a65acbd4854a1 -size 33816 +oid sha256:3f3925238751bd1cfd9b145a7bc3a12945392434b7ea3f345bc822116f4f5a51 +size 44553 diff --git a/Game/GigLine/Content/Hollywood/Site/Crossing_Markings.uasset b/Game/GigLine/Content/Hollywood/Site/Crossing_Markings.uasset index b85f6c7..4c3e2c1 100644 --- a/Game/GigLine/Content/Hollywood/Site/Crossing_Markings.uasset +++ b/Game/GigLine/Content/Hollywood/Site/Crossing_Markings.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:27a75622ca7ea90976cd88677961ca6d195b61e965a0ff17d6c8909b012918af -size 74929 +oid sha256:70c729bb152a8bbd7cfe2f457e0d3fa1b1bac455d696bb27d23644aecff7cecd +size 75501 diff --git a/Game/GigLine/Content/Hollywood/Site/Kerb_North.uasset b/Game/GigLine/Content/Hollywood/Site/Kerb_North.uasset index 10a7d05..88cb645 100644 --- a/Game/GigLine/Content/Hollywood/Site/Kerb_North.uasset +++ b/Game/GigLine/Content/Hollywood/Site/Kerb_North.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ddadef2aaa688c7525f87d4498949ea64bf9f0e463bc0118bd86a3989a2f5acd +oid sha256:7893216176a7977d4784234bf10e6b49a219ab31dd61497f8db7958c5a04b7ce size 83550 diff --git a/Game/GigLine/Content/Hollywood/Site/Kerb_South.uasset b/Game/GigLine/Content/Hollywood/Site/Kerb_South.uasset index 3a001b0..7f5ec1b 100644 --- a/Game/GigLine/Content/Hollywood/Site/Kerb_South.uasset +++ b/Game/GigLine/Content/Hollywood/Site/Kerb_South.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e13d22b4ae7ed978c6755adfd5b8c02d98157177976f792794c083b95797270 +oid sha256:fd5493759b160992ebec5073e59d67a6ea8ceeb1fbb35b0624223668934cebc3 size 83797 diff --git a/Game/GigLine/Content/Hollywood/Site/M_Asphalt.uasset b/Game/GigLine/Content/Hollywood/Site/M_Asphalt.uasset new file mode 100644 index 0000000..bb6d30d --- /dev/null +++ b/Game/GigLine/Content/Hollywood/Site/M_Asphalt.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fc0d523f0fb587306a9e946e27b10113eb89df1218e6fdb8553b29f083384e49 +size 4605 diff --git a/Game/GigLine/Content/Hollywood/Site/M_Concrete.uasset b/Game/GigLine/Content/Hollywood/Site/M_Concrete.uasset new file mode 100644 index 0000000..fa49e20 --- /dev/null +++ b/Game/GigLine/Content/Hollywood/Site/M_Concrete.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c765035f6c076c4016831884b167fa93e162bab7d0038f55cd09d240105dfa4c +size 4610 diff --git a/Game/GigLine/Content/Hollywood/Site/M_Ground.uasset b/Game/GigLine/Content/Hollywood/Site/M_Ground.uasset new file mode 100644 index 0000000..5948d47 --- /dev/null +++ b/Game/GigLine/Content/Hollywood/Site/M_Ground.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21454ed393d70dc103e0ad7ca00b06aed6fded381062ddcde87a424c05ff580d +size 4600 diff --git a/Game/GigLine/Content/Hollywood/Site/M_RoadPaint.uasset b/Game/GigLine/Content/Hollywood/Site/M_RoadPaint.uasset new file mode 100644 index 0000000..7aa4d2f --- /dev/null +++ b/Game/GigLine/Content/Hollywood/Site/M_RoadPaint.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:891ee60d2feac3e741509e9e03756e9941b4561994e1a77732f565b7aae5bb52 +size 4615 diff --git a/Game/GigLine/Content/Hollywood/Site/Road_Hollywood.uasset b/Game/GigLine/Content/Hollywood/Site/Road_Hollywood.uasset index 3809334..908fab3 100644 --- a/Game/GigLine/Content/Hollywood/Site/Road_Hollywood.uasset +++ b/Game/GigLine/Content/Hollywood/Site/Road_Hollywood.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20165ed97f13fb5fba8391ca8735d9cf965eb97d676282d549afafb1a274b0ca +oid sha256:5b2c133f3dc163f937cc7f73de9ee35a31659451e43171abdaa1b1fc5b357e1a size 309810 diff --git a/Game/GigLine/Content/Hollywood/Site/Sidewalk_North.uasset b/Game/GigLine/Content/Hollywood/Site/Sidewalk_North.uasset index 0ad1590..1416015 100644 --- a/Game/GigLine/Content/Hollywood/Site/Sidewalk_North.uasset +++ b/Game/GigLine/Content/Hollywood/Site/Sidewalk_North.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e32a1f9590b943a40f05efbd531ec4e1a6c6933c579a5dec4a3dca8753e408c0 +oid sha256:a3fa880ce24eff71415cf8039b9e2347181087c2fa42ac217b022a55e1cc68a8 size 150676 diff --git a/Game/GigLine/Content/Hollywood/Site/Sidewalk_South.uasset b/Game/GigLine/Content/Hollywood/Site/Sidewalk_South.uasset index 5b0956e..9b7eae9 100644 --- a/Game/GigLine/Content/Hollywood/Site/Sidewalk_South.uasset +++ b/Game/GigLine/Content/Hollywood/Site/Sidewalk_South.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d66130107a5d5d996d8367cc1cbb55c7ef506a91df419f0c9ad5ee74d652b915 +oid sha256:bf71bb2cd8feebb44a102f9a4f44179a5d26d7fb125cf5fbec31d5084acbd9c4 size 164347 diff --git a/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset b/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset index bd1a338..7d7ac7d 100644 --- a/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset +++ b/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d5d3a6fb6646ebeef426b7c07a58b872060779e6ffe0c8b073939ee93299ab8 -size 333138 +oid sha256:aa5407698175d4ca2a8e0ee7e3bdde23654337445260c9b314a62667d8edcdc1 +size 335528 diff --git a/Game/GigLine/Content/Test/ReviewMap.umap b/Game/GigLine/Content/Test/ReviewMap.umap index 8db2af7..1f0d7e8 100644 --- a/Game/GigLine/Content/Test/ReviewMap.umap +++ b/Game/GigLine/Content/Test/ReviewMap.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d399b3e1b28616e58f445fca8a7de82e9bf1df7c4308500473fc0c63d6d597d +oid sha256:18fafef3a73b195fdbc21ff1c6e9a8d55f691ae3626e2f588d64871554297b31 size 28613 diff --git a/MEMORY.md b/MEMORY.md index 0e5e9b1..e19febc 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -174,3 +174,12 @@ Scope: facts and traps that cost time to establish. Planned work lives in [TODO. - **`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). Use the `LevelEditorSubsystem`: `unreal.get_editor_subsystem(unreal.LevelEditorSubsystem).load_level(path)` actually switches. Symptom was ReviewMap containing Demo actors + `ShotSequencer`. - **Map-save ordering bug in `Tools/ue_build_review_map.py`**: the script saved ReviewMap *after* the DemoMap section, but the switch back to ReviewMap fails silently because the map only exists in memory at that point — so the final `save_map` wrote the demo world over ReviewMap. Fix: save ReviewMap first, then stage the demo cameras, and never switch back. Both maps verified by probe (ReviewMap lists Review actors; DemoMap lists Demo actors). - Rebuilt ReviewMap after the fixes: character, both cubes (A grounded, B floating at 2 m), pawn, NPC prefab, hero facade all present; user re-approved the shots. README cube naming corrected (README had A/B swapped vs the script). + +## 2026-11-07 — Materials and lighting in the demo map + +- **Blender flat diffuse colours do not survive FBX -> Unreal.** Every site mesh (road, sidewalks, kerbs, crossing paint) imported with a material asset that had a default *white* base colour, and the terrain imported with `WorldGridMaterial` (UE's bright grey grid). That — not the sun — is what made the demo look blown out and washed white. Fix: create the colours in UE (`M_Asphalt`, `M_Concrete`, `M_Ground`, `M_RoadPaint` via `Constant3Vector`) and assign them per actor in `Tools/ue_build_demo_map.py`. +- **Auto-exposure makes review shots non-comparable**: the same road rendered washed-out in one framing and near-black in another because each camera saw a different amount of sky. Fix: an unbound `PostProcessVolume` with `AEM_HISTOGRAM` and `auto_exposure_min_brightness == auto_exposure_max_brightness` (fixed exposure). Do not use `AEM_MANUAL` + `auto_exposure_bias` — a bias of 11 blew the whole frame out. +- **`DirectionalLightComponent.light_color` takes a `Color` struct, not `LinearColor`** (`unreal.Color(r=255,g=245,b=230,a=255)`); passing `LinearColor` raises a nativize error. +- **One band per object loses geometry downstream.** `crossing_band()` created `Crossing_Markings` each call, so Blender renamed the second and third to `.001`/`.002`; `site_assembly.py` appends by exact name, so only one crossing ever reached the game. Build all bands into a single mesh instead of one object per band. +- **Street arc length -> UE X:** s=0 is the west end of the block (UE X −27966 cm) and s increases eastward, so the designated mid-block crossing at s=130.1 m is UE X ≈ −14956, not the −24291 used by an earlier camera (that is the s=36.6 crossing). +- **Camera placement in the demo map needs the mesh bounds, not intuition.** The hero façade body spans UE Y −1457..−200, so a "street level" camera at Y −400 is *inside* the building and renders its unlit back face. Read `get_bounding_box()` on the staged meshes before choosing a camera. diff --git a/Screenshots/README.md b/Screenshots/README.md index a022080..c2e6285 100644 --- a/Screenshots/README.md +++ b/Screenshots/README.md @@ -22,8 +22,14 @@ and exits — about a minute. The camera list lives in - **Floor**: 160 m square, top face at Z 0. The character measures Z 0..83 cm. - **Cubes**: `Cube_ScaleTest_A` rests on the floor, `Cube_ScaleTest_B` floats at Z 2 m; both are exactly 1 m — the scale reference. -- **Lighting**: one warm key light plus sky fill (review rig, not the `0.1.0` - daytime look). Beyond the floor is open: the horizon is a black band. +- **Lighting (review map)**: one warm key light plus sky fill, neutral exposure. +- **Lighting (demo map)**: fixed daytime — sun at pitch -50 / yaw 120 (intensity + 4, warm), sky light 2.6, sky atmosphere, height fog, and a manual-exposure + post-process volume (min = max brightness 1.0) so a shot cannot wash out or + crush to black depending on how much sky it frames. Surface colours are + assigned explicitly in `Tools/ue_build_demo_map.py` (`M_Asphalt`, `M_Concrete`, + `M_Ground`, `M_RoadPaint`) because Blender's flat diffuse colours do not + survive the FBX → Unreal import. ## Review-map shots @@ -53,10 +59,10 @@ Rendered from `/Game/Hollywood/DemoMap`, the walkable level. Click an image for | File | Preview | Approval | What it shows | | --- | --- | --- | --- | -| `demo/review-demo-spawn-view.png` | [spawn view](demo/review-demo-spawn-view.png) | ☑ | Spawn view: north sidewalk, the hero façade across the road | -| `demo/review-demo-street-level.png` | [street level](demo/review-demo-street-level.png) | ☑ | Street level: portals, flights, banners, colonnade | -| `demo/review-demo-crossing.png` | [crossing](demo/review-demo-crossing.png) | ☑ | The designated mid-block crossing (s=130 m): zebra markings on the road, kerbs flushed to ramps. Camera on the north sidewalk looking south-west. Note: the road renders near-black in shadow — the fixed-daytime-lighting item, not the markings | -| `demo/review-demo-north-buildings.png` | [north buildings](demo/review-demo-north-buildings.png) | ☑ | North-side background buildings (TCL, Tussaud's) across the Blvd | +| `demo/review-demo-spawn-view.png` | [spawn view](demo/review-demo-spawn-view.png) | needs review | Spawn view from the north sidewalk: the hero façade across the road under fixed daytime light | +| `demo/review-demo-street-level.png` | [street level](demo/review-demo-street-level.png) | needs review | Street level from the north sidewalk: the façade colonnade and columns at eye height, now sunlit with cast shadows (was backlit and near-black) | +| `demo/review-demo-crossing.png` | [crossing](demo/review-demo-crossing.png) | needs review | The designated mid-block crossing at s=130 m (UE X -14956): three continental zebra bars on the dark asphalt, kerbs flushed to ramps, camera on the north sidewalk looking along the crosswalk | +| `demo/review-demo-north-buildings.png` | [north buildings](demo/review-demo-north-buildings.png) | needs review | North-side background buildings (TCL, Tussaud's) across the Blvd, lit against the sky | ## What to look for diff --git a/Screenshots/demo/review-demo-crossing.png b/Screenshots/demo/review-demo-crossing.png index 041f7ed..f9523d8 100644 --- a/Screenshots/demo/review-demo-crossing.png +++ b/Screenshots/demo/review-demo-crossing.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6bd66678c132a8fa09004f2f2e3d160b1c99122e1d1365932d7e31ed0933199 -size 1628670 +oid sha256:90b3f9e5aa44b2bd5f446c0532e1ecc7947e005188e909748f1d6520abf1648d +size 1568849 diff --git a/Screenshots/demo/review-demo-north-buildings.png b/Screenshots/demo/review-demo-north-buildings.png index 137a99a..4ffe045 100644 --- a/Screenshots/demo/review-demo-north-buildings.png +++ b/Screenshots/demo/review-demo-north-buildings.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5eaa60227e5f6e307e1bf878b33d3f52634e0121dcaa6af73afd7fff5728499b -size 2304425 +oid sha256:1b3eb0d7831dcd3fe83cf2e0b65dc6b8f20b21ebf3d03ae538ad60565f5a5037 +size 1390196 diff --git a/Screenshots/demo/review-demo-spawn-view.png b/Screenshots/demo/review-demo-spawn-view.png index 9132d32..9635de1 100644 --- a/Screenshots/demo/review-demo-spawn-view.png +++ b/Screenshots/demo/review-demo-spawn-view.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a312b5ee86cd5c9fef8b686d2aae429199f88caae7276d6fb714d1d73fed996a -size 2571043 +oid sha256:a9106c7c915119e4df4bedc22404028ed060a71e309ff1662931890b55469644 +size 1573928 diff --git a/Screenshots/demo/review-demo-street-level.png b/Screenshots/demo/review-demo-street-level.png index 86a9834..22d99fc 100644 --- a/Screenshots/demo/review-demo-street-level.png +++ b/Screenshots/demo/review-demo-street-level.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2675eb353573003961c8d81ba24c2bbbe1f1052c11c4ee38cd786cab7115b686 -size 1975605 +oid sha256:fd5e854bfce63b51d573817bd86ca897b06cbf231b02c0302f7e99b1bb7c6870 +size 1783060 diff --git a/Screenshots/review-character.png b/Screenshots/review-character.png index b550695..7aed33b 100644 --- a/Screenshots/review-character.png +++ b/Screenshots/review-character.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea60ed98f688bab482160386d851b39a10868c64bf1371b3628427cc25a3de97 -size 2498425 +oid sha256:f314dcc5211758e70c0fde67ece08eaeef863cc27596eefb15fd706ed20d6ce8 +size 2476490 diff --git a/Screenshots/review-ground-contact.png b/Screenshots/review-ground-contact.png index 2c74a02..04fba20 100644 --- a/Screenshots/review-ground-contact.png +++ b/Screenshots/review-ground-contact.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:03b47847338f6d840e1e2dd370fce585c599cd72db0cf1393b82cc91964619c0 -size 1778455 +oid sha256:ef80c5c6eb63b79e97ba41d8a79793b2215b71c854966c093904016aa3eb47da +size 1793304 diff --git a/Screenshots/review-hero-facade-front.png b/Screenshots/review-hero-facade-front.png index 564b65c..24fbb72 100644 --- a/Screenshots/review-hero-facade-front.png +++ b/Screenshots/review-hero-facade-front.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fabcc154b139f35e0c6910f87a042c6cc51b9e263993cbfb49a15fa3817736d3 -size 2052242 +oid sha256:6a6b384911d4f07d1e165545ee6c78338869df8a8d30231d1194e7b89a6349df +size 2051886 diff --git a/Screenshots/review-hero-facade-oblique.png b/Screenshots/review-hero-facade-oblique.png index 07d778c..1da0b73 100644 --- a/Screenshots/review-hero-facade-oblique.png +++ b/Screenshots/review-hero-facade-oblique.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20a85e17ab952b5f8de52556bbe9bf7b4dfb03c7e430a371eafa625b0874b4d7 -size 1917582 +oid sha256:9d47147cd5b8c4959ce61da5b7ea72c6bf4dab21bebfd9a2426809f873e72938 +size 1932092 diff --git a/Screenshots/review-hero-portals.png b/Screenshots/review-hero-portals.png index 69194a1..87dcc4a 100644 --- a/Screenshots/review-hero-portals.png +++ b/Screenshots/review-hero-portals.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8fa065612a714476d37f5cc4bb28182b30dc9b10cfcfd37cc04257d177b41f33 -size 1774919 +oid sha256:ce2496abd2848ae5d73c17e4eb7c2ccb9bd39437f0f47ee1f2e81b75a64974c6 +size 1778660 diff --git a/Screenshots/review-npc-prefab.png b/Screenshots/review-npc-prefab.png index b10ff0f..3003951 100644 --- a/Screenshots/review-npc-prefab.png +++ b/Screenshots/review-npc-prefab.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1522a746881f69e8fb2e327610ad9fd7d98e673c200ea8d781daab1f23317cc -size 1721860 +oid sha256:8de11af89103a6ab42531adbcab2985e29f239e0a2122972d1308d1ca98edba3 +size 1721002 diff --git a/Screenshots/review-overview.png b/Screenshots/review-overview.png index d518bc4..76dcd69 100644 --- a/Screenshots/review-overview.png +++ b/Screenshots/review-overview.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78b318299a7633fb267c961ecfd61f5fe7a0a3278d28103b634d21f47bc01c49 -size 2580045 +oid sha256:50c31d72e9149efbd61e4dd454f2671342162a1a3aa58140c1427d523e2091a5 +size 2608909 diff --git a/Screenshots/review-player-placement.png b/Screenshots/review-player-placement.png index f39f1df..d86b002 100644 --- a/Screenshots/review-player-placement.png +++ b/Screenshots/review-player-placement.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:605fc1ee8ef167a57d119ef83e741ab1e6d8fbba923bfe5aa162f0dda1069830 -size 1969709 +oid sha256:4c21d2f5f92612cca6963b3217ae9f2ebf45c94837a09b6f0e8398622a9bc040 +size 1964636 diff --git a/Screenshots/review-scale-cubes.png b/Screenshots/review-scale-cubes.png index 8fc47eb..9ca844c 100644 --- a/Screenshots/review-scale-cubes.png +++ b/Screenshots/review-scale-cubes.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26c64adad9f64789c85882a224d402ae4e8ca20eb73736731925972275fc560d -size 1906660 +oid sha256:daad42906dd5ee802051d09ca813a293a4b73927dd68adc876fc6612ce8ef951 +size 1902016 diff --git a/Screenshots/review-shadow-compare.png b/Screenshots/review-shadow-compare.png index 2d975fa..f5555bc 100644 --- a/Screenshots/review-shadow-compare.png +++ b/Screenshots/review-shadow-compare.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bab0f21cc8a6c90889e0003510b7df68f6f23d16e69e9ba08faec40deef77c8b -size 1735259 +oid sha256:79123a8f37fd95b617decda35469f7c629f89bbe5f64c063f8b087b27f2615ce +size 1731317 diff --git a/TODO.md b/TODO.md index e5d3374..7061a9d 100644 --- a/TODO.md +++ b/TODO.md @@ -95,7 +95,7 @@ The MPFB character asset is planned as the NPC template (see `ROADMAP.md`). Keep - [~] Collision, reset, and pause functionality. Reset (**R**) and pause (**Escape**, a real game pause that releases the cursor) are implemented in `AGigLineCharacter` with `/Game/Input/IA_Reset` and `IA_Pause`. Collision now exists — terrain, road and sidewalks import as complex-as-simple, so a simple hull cannot ramp over the kerbs. **Not verified by hand**: walk, reset and pause need a human at the keyboard. No pause menu widget yet, only the pause itself. - [x] Build the walkable demo level: terrain, street, and hero facade assembled into one map with a start point on the north sidewalk, so the demo is something a player can walk rather than a review staging. **Built 2026-11-07** by `Tools/ue_build_demo_map.py` → `/Game/Hollywood/DemoMap`: sun/sky, `Hero_Facade`, six site meshes imported with collision (`Terrain_DEM`, `Road_Hollywood`, both sidewalks and both kerbs), and a `Demo_PlayerStart` on the north sidewalk 20 m back from the frontage. Demo views in `Screenshots/demo/` re-rendered and ☑ approved 2026-11-07. - [x] Walkable sidewalks and a designated street crossing. Crossing chosen: mid-block signalised crossing, OSM way 385648319, at 130 m along the block (`Geo/verification/2026-11-06-street-profile.md`). Done 2026-11-07: continental zebra markings (0.5 m bars / 0.5 m gaps, 3 m band) at all three signalised crossings, no collision on the paint; kerbs were already flushed at the crossings. Enablers found on the way: the street surfaces were 100 % down-facing (invisible in engine) — winding flipped; the terrain buried the street — 10.7 m corridor cut around the centreline (sidewalks cover the transition, walk-check 0 holes); LFS-lockable `.blend`/`.umap` check out read-only — builders now chmod before saving and fail loudly. Screenshot ☑ approved 2026-11-07 (`Screenshots/demo/review-demo-crossing`); the corridor renders near-black in shadow — that is the still-open **Fixed daytime lighting** item, not the crossing. -- [-] Fixed daytime lighting and basic ambience. Current state: `Demo_Sun` at pitch −35, yaw 200 puts the street corridor in shadow — the crossing shot renders near-black. Plan: rotate sun yaw so light comes down the street axis (e/w), raise sun intensity, boost skylight fill, set fixed daytime (no time-of-day motion). +- [-] Fixed daytime lighting and basic ambience. **Implemented 2026-11-07**, pending screenshot approval. Demo map now has a fixed daytime rig in `Tools/ue_build_demo_map.py`: sun pitch −50 / yaw 120 (intensity 4, warm 255/245/230, atmosphere sun light), sky light 2.6 with real-time capture, sky atmosphere, height fog (density 0.008), and a manual-exposure post-process volume (min = max brightness 1.0) so a shot cannot wash out or crush to black. Two root causes found while doing it: (a) **Blender flat diffuse colours do not survive the FBX→UE import** — every site mesh arrived white and the terrain arrived with `WorldGridMaterial`, which is what actually made the scene look blown out; explicit `M_Asphalt` / `M_Concrete` / `M_Ground` / `M_RoadPaint` materials are now created and assigned in the demo builder. (b) only **one of the three crossings** was reaching the game: each band was its own object and Blender renamed the duplicates `.001`/`.002`, which site assembly skips; all bands are now one `Crossing_Markings` object. Demo cameras also fixed: street-level sat inside the façade body, and the crossing camera aimed at the wrong crossing (s=36.6 instead of the designated s=130.1 at UE X −14956). - [ ] Geographic attribution display ("© OpenStreetMap contributors" + license link). ### Repository publication diff --git a/Tools/ue_build_demo_map.py b/Tools/ue_build_demo_map.py index a79107d..cbbde45 100644 --- a/Tools/ue_build_demo_map.py +++ b/Tools/ue_build_demo_map.py @@ -50,6 +50,29 @@ def spawn(cls, loc, label, rot=None): return actor +def ensure_material(asset_name, rgb): + """Create/reuse a flat-colour material under /Game/Hollywood/Site. + + Used where the FBX material round-trip does not survive import: the terrain + arrives with WorldGridMaterial (UE's bright default grid), which reads as a + blown-out white field under the demo sun. + """ + path = f"/Game/Hollywood/Site/{asset_name}" + if unreal.EditorAssetLibrary.does_asset_exist(path): + return unreal.EditorAssetLibrary.load_asset(path) + mat = unreal.AssetToolsHelpers.get_asset_tools().create_asset( + asset_name, "/Game/Hollywood/Site", unreal.Material, unreal.MaterialFactoryNew()) + node = unreal.MaterialEditingLibrary.create_material_expression( + mat, unreal.MaterialExpressionConstant3Vector) + node.set_editor_property("constant", unreal.LinearColor(rgb[0], rgb[1], rgb[2], 1.0)) + unreal.MaterialEditingLibrary.connect_material_property( + node, "", unreal.MaterialProperty.MP_BASE_COLOR) + unreal.MaterialEditingLibrary.recompile_material(mat) + unreal.EditorAssetLibrary.save_loaded_asset(mat) + log(f"created material {asset_name} rgb={rgb}") + return mat + + def clear(prefixes=()): removed = 0 for actor in unreal.EditorLevelLibrary.get_all_level_actors(): @@ -74,12 +97,87 @@ def build(): removed = clear(("Demo_", "PlayerStart", "Hero_Facade")) log(f"cleared {removed} previous demo actors") - # --- lighting --------------------------------------------------------- - spawn(unreal.DirectionalLight, unreal.Vector(0, 0, 2000), "Demo_Sun", - unreal.Rotator(pitch=-35.0, yaw=200.0, roll=0.0)) - spawn(unreal.SkyLight, unreal.Vector(0, 0, 2000), "Demo_SkyLight") + # --- lighting: fixed daytime ------------------------------------------ + # The demo is a fixed daytime scene, not a time-of-day simulation. Three + # things matter and each was wrong before: + # + # 1. Sun angle. The hero facade front faces -Y and the road faces +Z. A sun + # at yaw 120 / pitch -50 shines toward (-X, +Y, down), so it lights both + # the facade front and the carriageway instead of raking across them. + # 2. Exposure. Auto-exposure made the same road washed-out in one shot and + # near-black in the next, because each camera framed a different amount + # of sky. A manual-exposure post-process volume pins it. + # 3. Ambience. A height fog gives the street depth and hides the terrain + # edge that otherwise reads as a black band at the horizon. + sun = spawn(unreal.DirectionalLight, unreal.Vector(0, 0, 2000), "Demo_Sun", + unreal.Rotator(pitch=-50.0, yaw=120.0, roll=0.0)) + if sun: + comp = sun.get_component_by_class(unreal.DirectionalLightComponent) + if comp: + for prop, value in ( + ("intensity", 4.0), + ("light_color", unreal.Color(r=255, g=245, b=230, a=255)), + ("atmosphere_sun_light", True), + ): + try: + comp.set_editor_property(prop, value) + except Exception as exc: # noqa: BLE001 + log(f"sun.{prop} not set: {exc!r}") + log("staged: Demo_Sun (pitch -50, yaw 120, intensity 4, warm)") + + sky = spawn(unreal.SkyLight, unreal.Vector(0, 0, 2000), "Demo_SkyLight") + if sky: + comp = sky.get_component_by_class(unreal.SkyLightComponent) + if comp: + for prop, value in (("intensity", 2.6), ("real_time_capture", True)): + try: + comp.set_editor_property(prop, value) + except Exception as exc: # noqa: BLE001 + log(f"sky.{prop} not set: {exc!r}") + log("staged: Demo_SkyLight (intensity 2.6, real-time capture)") + spawn(unreal.SkyAtmosphere, unreal.Vector(0, 0, 0), "Demo_Sky") - log("staged: sun, sky light, sky atmosphere") + log("staged: Demo_Sky (sky atmosphere)") + + fog = spawn(unreal.ExponentialHeightFog, unreal.Vector(0, 0, 0), "Demo_Fog") + if fog: + comp = fog.get_component_by_class(unreal.ExponentialHeightFogComponent) + if comp: + for prop, value in ( + ("fog_density", 0.008), + ("fog_height_falloff", 0.15), + ("start_distance", 3000.0), + ): + try: + comp.set_editor_property(prop, value) + except Exception as exc: # noqa: BLE001 + log(f"fog.{prop} not set: {exc!r}") + log("staged: Demo_Fog (height fog, density 0.008)") + + # Fixed exposure: without this the same road renders washed out or black + # depending on how much sky the camera frames. Pinning min == max brightness + # freezes the exposure at one value (the classic fixed-exposure trick); + # AEM_MANUAL with a bias was tried first and blew the frame out. + ppv = spawn(unreal.PostProcessVolume, unreal.Vector(0, 0, 0), "Demo_PostProcess") + if ppv: + ppv.set_editor_property("unbound", True) + settings = ppv.get_editor_property("settings") + for prop, value in ( + ("override_auto_exposure_method", True), + ("auto_exposure_method", unreal.AutoExposureMethod.AEM_HISTOGRAM), + ("override_auto_exposure_min_brightness", True), + ("auto_exposure_min_brightness", 1.0), + ("override_auto_exposure_max_brightness", True), + ("auto_exposure_max_brightness", 1.0), + ("override_auto_exposure_bias", True), + ("auto_exposure_bias", 0.0), + ): + try: + settings.set_editor_property(prop, value) + except Exception as exc: # noqa: BLE001 + log(f"pp.{prop} not set: {exc!r}") + ppv.set_editor_property("settings", settings) + log("staged: Demo_PostProcess (fixed exposure, min=max brightness 1.0)") # --- hero facade on the measured building line ------------------------ hero = unreal.EditorAssetLibrary.load_asset("/Game/Hollywood/ElCapitan/SM_HeroFacade") @@ -93,9 +191,21 @@ def build(): log("MISSING: SM_HeroFacade — run Tools/ue_hero_facade_import.py") # --- site meshes, if they have been imported -------------------------- + # Blender's flat diffuse colours do not survive FBX -> Unreal: Interchange + # builds a material with a default white base colour, and the terrain even + # lands with WorldGridMaterial. Every site surface therefore rendered white, + # which is what washed out the demo. Assign explicit colours per mesh here. + SURFACE_MATERIALS = { + "Terrain_DEM": ("M_Ground", (0.30, 0.28, 0.25)), + "Road_Hollywood": ("M_Asphalt", (0.055, 0.055, 0.06)), + "Sidewalk_South": ("M_Concrete", (0.52, 0.50, 0.47)), + "Sidewalk_North": ("M_Concrete", (0.52, 0.50, 0.47)), + "Kerb_South": ("M_Concrete", (0.52, 0.50, 0.47)), + "Kerb_North": ("M_Concrete", (0.52, 0.50, 0.47)), + "Crossing_Markings": ("M_RoadPaint", (0.80, 0.80, 0.78)), + } imported = 0 - for name in ("Terrain_DEM", "Road_Hollywood", "Sidewalk_South", "Sidewalk_North", - "Kerb_South", "Kerb_North", "Crossing_Markings"): + for name, (mat_name, rgb) in SURFACE_MATERIALS.items(): mesh = unreal.EditorAssetLibrary.load_asset(f"/Game/Hollywood/Site/{name}") if mesh is None: continue @@ -103,8 +213,11 @@ def build(): if actor: actor.static_mesh_component.set_static_mesh(mesh) actor.static_mesh_component.set_mobility(unreal.ComponentMobility.STATIC) + mat = ensure_material(mat_name, rgb) + if mat: + actor.static_mesh_component.set_material(0, mat) imported += 1 - log(f"staged {imported} site meshes from /Game/Hollywood/Site") + log(f"staged {imported} site meshes with explicit surface materials") # --- player start: north sidewalk, facing the hero frontage ------------- # Signs were measured from the imported meshes rather than assumed: the FBX diff --git a/Tools/ue_build_review_map.py b/Tools/ue_build_review_map.py index bd48a94..edd3ce9 100644 --- a/Tools/ue_build_review_map.py +++ b/Tools/ue_build_review_map.py @@ -258,8 +258,17 @@ try: # 14 m back so the 14.5 m elevation fits: at 2 m the camera sees only # the colonnade's underside. ("demo-spawn-view", unreal.Vector(-10722.5, -3500, 165), unreal.Vector(-10722.5, -1450, 700)), - ("demo-street-level", unreal.Vector(-11000, -400, 165), unreal.Vector(-10722.5, -1450, 500)), - ("demo-crossing", unreal.Vector(-23750, -1800, 480), unreal.Vector(-24291, -1400, 300)), + # Street level, from the north sidewalk: the old camera sat at Y -400, + # inside the facade body (which spans Y -1457..-200), so it looked at + # the unlit back face. Y -2600 is on the sidewalk in front of the lit + # elevation. + ("demo-street-level", unreal.Vector(-10850, -2600, 170), unreal.Vector(-10722.5, -1450, 400)), + # The designated mid-block crossing. Street arc length runs from the + # west end (s=0 at X -27966 cm), so s=130.1 m is UE X -14956, not the + # -24291 the old camera used (that is the s=36.6 crossing). Camera sits + # on the north sidewalk looking along the crosswalk itself, which is + # the pedestrian's view and shows the zebra bars as bars. + ("demo-crossing", unreal.Vector(-15250, -2500, 400), unreal.Vector(-14956, 400, 150)), # the two north-side background buildings (TCL, Tussaud's) seen from # the south sidewalk, across Hollywood Blvd: their frontage and mass. ("demo-north-buildings", unreal.Vector(-22800, -1000, 400), unreal.Vector(-22800, 3000, 300)),