From eb614fe0350d541d464c69adaa45ac59c7d75bf8 Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Sat, 7 Nov 2026 15:23:06 +0100 Subject: [PATCH 1/8] todo: claim Gameplay section on branch gameplay-crossing-0.1.0 --- TODO.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index 31b9afa..784e07f 100644 --- a/TODO.md +++ b/TODO.md @@ -69,7 +69,7 @@ Owner: this session. Scope: the review scene used for all visual approval — th - [x] Extend the review floor beyond the hero façade: 160 m square centred on the façade, so façade shots stand on ground instead of the horizon band. - [x] Delete retired shots: the old `first-person` frame is gone and the camera list is now a single table so a change cannot leave an orphan. -### Character development +### Character development - active - 01a1b26b-3c94-707f-9665-2f6b4a79b564 The MPFB character asset is planned as the NPC template (see `ROADMAP.md`). Keep it as a standalone character/prefab, not wired into the `AGigLineCharacter` pawn. @@ -88,7 +88,7 @@ The MPFB character asset is planned as the NPC template (see `ROADMAP.md`). Keep - [-] Build sidewalks, roads, and collision meshes. Terrain, road, sidewalks and kerbs exist (`Art/blender/terrain/`, `street/`) and are assembled in `site/site-2026-11-06.blend`; collision and details (crossing markings, kerb radii, aprons) are not built. Ground must follow the DEM: 3.94 m fall along the block, ~2 % crown between the gutters at ±10–11 m from the centreline, north sidewalk higher than south. Kerb height is an unresolved assumption — the DEM does not resolve it (`Geo/verification/2026-11-06-cross-sections.md`). - [ ] Materials and look development for all exported assets. -### Gameplay - active - gig-line-gameplay +### Gameplay - active - 01a1b26b-3c94-707f-9665-2f6b4a79b564 (branch `gameplay-crossing-0.1.0`) - [x] First-person movement, mouse look, and Enhanced Input bindings. Implemented 2026-11-07: `AGigLineCharacter` (C++, 1.75 m capsule, 1.60 m eye camera, 4 m/s walk, mouse look in deg/s, jump) as the default pawn, with five actions and an 11-entry context in `/Game/Input` built by `Tools/ue_create_input_assets.py`. **Not yet confirmed interactively** — launch PIE on `Test/ReviewMap` and walk. Screenshot review pending (`Screenshots/README.md`). - [~] 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. From cef87b3d31bd115acd677cf7adf6a244f7ead0d1 Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Sat, 7 Nov 2026 15:24:21 +0100 Subject: [PATCH 2/8] street: continental zebra markings at the three signalised crossings 0.5 m white bars with 0.5 m gaps across a 3 m band per crossing, laid 1 cm above the road surface. Makes the mid-block crossing (OSM way 385648319, s=130.1 m) a designated crossing in game. Metadata audit passes (1 sidecar, 0 problems). --- Art/blender/street/street-2026-11-06.blend | 4 +-- Art/blender/street/street_mesh.py | 42 ++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/Art/blender/street/street-2026-11-06.blend b/Art/blender/street/street-2026-11-06.blend index 47871e3..f186e53 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:b675a61e3f6d2d97d77383ff2edc522886bbee9fbb84a3ebccdce2e69ba1ba08 -size 510663 +oid sha256:8ae571a174d7c07f01ebd90d60ef4c3ffaacc11d0fc8476c6d85eb00acb8fbbd +size 512324 diff --git a/Art/blender/street/street_mesh.py b/Art/blender/street/street_mesh.py index db900e1..cdd99e9 100644 --- a/Art/blender/street/street_mesh.py +++ b/Art/blender/street/street_mesh.py @@ -18,6 +18,10 @@ Geometry follows the measured profile rather than a guess: kerb (see `Geo/verification/2026-11-06-cross-sections.md`); * kerbs are flushed to 0.02 m within ±2.5 m of each signalised crossing, with a 1.5 m blend, so the crossings are actually usable on foot; +* each signalised crossing gets continental zebra markings — 0.5 m white bars + with 0.5 m gaps across a 3 m band, laid 1 cm above the road surface — so the + mid-block crossing (OSM way 385648319, s ≈ 130 m) reads as a designated + crossing in game; * sidewalk widths 4.0 m south and 5.3 m north, from the DEM-derived setback arithmetic; * the DEM supplies every elevation, so the 3.4 m fall and the ~2 % crown come @@ -247,6 +251,44 @@ def main(): kerb_faces(SOUTH_GUTTER, "Kerb_South") kerb_faces(NORTH_GUTTER, "Kerb_North") + # Continental zebra markings: one band per signalised crossing. Bars run + # along the road (traffic direction), side by side across the carriageway; + # a pedestrian crosses perpendicular to them. Lifted 1 cm above the road + # surface (which is itself 2 cm above the DEM) to avoid z-fighting. + paint = bpy.data.materials.new("Road_Paint_White") + paint.diffuse_color = (0.85, 0.85, 0.85, 1.0) + + def crossing_band(s_c): + 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)) + for k in range(n): + s0 = s_c - zone / 2 + k * (w + g) + corners = [] + for s_edge in (s0, s0 + w): + base, normal, _ = at(s_edge) + for off in span: + x = base[0] + off * normal[0] + y = base[1] + off * normal[1] + corners.append((len(verts), (x, y, (dem_z(x, y) or 0.0) + 0.03))) + # 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[1], idx[3], idx[2])) + 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 + + for s_c in CROSSINGS: + band = crossing_band(s_c) + print(f"crossing band at s={s_c:.1f}: {len(band.data.polygons)} stripes") + 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}") From 985dfcbac7c0d155add6ff753315d37b74002354 Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Sat, 7 Nov 2026 15:28:42 +0100 Subject: [PATCH 3/8] npc: AGigLineNPCPerson prefab + stable-named screenshots - New C++ AGigLineNPCPerson: reusable NPC/pedestrian prefab built on the MPFB human. ACharacter capsule half-height 83 cm (total 1.66 m, head top ~1.66 m so a 1.60 m camera doesn't clip), the character skeletal mesh on the built-in mesh as the visible body (feet on Z 0), AutoPossessAI for 0.5.0 pedestrians. Staged at Z 0 in the review scene; captured as review-npc-prefab.png. - AGigLineShotActor now names screenshots by stable camera name (no list-index prefix): inserting or reordering cameras no longer renumbers files or orphans the old ones. Re-rendered all review/demo shots with stable names. - ue_build_review_map.py: chmod the target .umap before save_map (git marks maps 444 on checkout, silently making save_map return False and dropping stages). - Screenshots/README.md, ROADMAP, TODO updated for the prefab + stable names. - DemoMap.umap rebuilt by the render pipeline (north buildings + demo cameras). --- Game/GigLine/Content/Hollywood/DemoMap.umap | 2 +- Game/GigLine/Content/Test/ReviewMap.umap | 4 +-- .../Source/GigLine/GigLineNPCPerson.cpp | 34 +++++++++++++++++++ .../GigLine/Source/GigLine/GigLineNPCPerson.h | 32 +++++++++++++++++ .../Source/GigLine/GigLineShotActor.cpp | 8 +++-- ROADMAP.md | 2 +- Screenshots/README.md | 33 ++++++++++-------- .../demo/review-00-demo-spawn-view.png | 3 -- .../demo/review-01-demo-street-level.png | 3 -- Screenshots/demo/review-02-demo-crossing.png | 3 -- .../demo/review-03-demo-north-buildings.png | 3 -- Screenshots/demo/review-demo-crossing.png | 3 ++ .../demo/review-demo-north-buildings.png | 3 ++ Screenshots/demo/review-demo-spawn-view.png | 3 ++ Screenshots/demo/review-demo-street-level.png | 3 ++ Screenshots/review-00-overview.png | 3 -- Screenshots/review-01-character.png | 3 -- Screenshots/review-02-scale-cubes.png | 3 -- Screenshots/review-03-player-placement.png | 3 -- Screenshots/review-04-ground-contact.png | 3 -- Screenshots/review-05-shadow-compare.png | 3 -- Screenshots/review-06-hero-portals.png | 3 -- Screenshots/review-07-hero-facade-front.png | 3 -- Screenshots/review-08-hero-facade-oblique.png | 3 -- Screenshots/review-character.png | 3 ++ Screenshots/review-ground-contact.png | 3 ++ Screenshots/review-hero-facade-front.png | 3 ++ Screenshots/review-hero-facade-oblique.png | 3 ++ Screenshots/review-hero-portals.png | 3 ++ Screenshots/review-npc-prefab.png | 3 ++ Screenshots/review-overview.png | 3 ++ Screenshots/review-player-placement.png | 3 ++ Screenshots/review-scale-cubes.png | 3 ++ Screenshots/review-shadow-compare.png | 3 ++ TODO.md | 19 ++++++----- Tools/ue_build_review_map.py | 31 +++++++++++++++++ 36 files changed, 178 insertions(+), 68 deletions(-) create mode 100644 Game/GigLine/Source/GigLine/GigLineNPCPerson.cpp create mode 100644 Game/GigLine/Source/GigLine/GigLineNPCPerson.h delete mode 100644 Screenshots/demo/review-00-demo-spawn-view.png delete mode 100644 Screenshots/demo/review-01-demo-street-level.png delete mode 100644 Screenshots/demo/review-02-demo-crossing.png delete mode 100644 Screenshots/demo/review-03-demo-north-buildings.png create mode 100644 Screenshots/demo/review-demo-crossing.png create mode 100644 Screenshots/demo/review-demo-north-buildings.png create mode 100644 Screenshots/demo/review-demo-spawn-view.png create mode 100644 Screenshots/demo/review-demo-street-level.png delete mode 100644 Screenshots/review-00-overview.png delete mode 100644 Screenshots/review-01-character.png delete mode 100644 Screenshots/review-02-scale-cubes.png delete mode 100644 Screenshots/review-03-player-placement.png delete mode 100644 Screenshots/review-04-ground-contact.png delete mode 100644 Screenshots/review-05-shadow-compare.png delete mode 100644 Screenshots/review-06-hero-portals.png delete mode 100644 Screenshots/review-07-hero-facade-front.png delete mode 100644 Screenshots/review-08-hero-facade-oblique.png create mode 100644 Screenshots/review-character.png create mode 100644 Screenshots/review-ground-contact.png create mode 100644 Screenshots/review-hero-facade-front.png create mode 100644 Screenshots/review-hero-facade-oblique.png create mode 100644 Screenshots/review-hero-portals.png create mode 100644 Screenshots/review-npc-prefab.png create mode 100644 Screenshots/review-overview.png create mode 100644 Screenshots/review-player-placement.png create mode 100644 Screenshots/review-scale-cubes.png create mode 100644 Screenshots/review-shadow-compare.png diff --git a/Game/GigLine/Content/Hollywood/DemoMap.umap b/Game/GigLine/Content/Hollywood/DemoMap.umap index 3b9b4fc..edb6e47 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:dd9704de1c462f323006ed352fbbb3833dd54cba728338476c60f91c4d4203d6 +oid sha256:d6eb86a071caaa75dd40d5f39e0746a1905844c3e479eb86c25081536f1acab9 size 32372 diff --git a/Game/GigLine/Content/Test/ReviewMap.umap b/Game/GigLine/Content/Test/ReviewMap.umap index 5a48dcc..461473e 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:deddb2f16424c6147e0fda196002fb44888f9055d34554dc5a567675f720c0d6 -size 28448 +oid sha256:4d2d5e8d826561523a076f0b6e11ab53d46d744b024311281b39a454f96f8f0c +size 32736 diff --git a/Game/GigLine/Source/GigLine/GigLineNPCPerson.cpp b/Game/GigLine/Source/GigLine/GigLineNPCPerson.cpp new file mode 100644 index 0000000..9700208 --- /dev/null +++ b/Game/GigLine/Source/GigLine/GigLineNPCPerson.cpp @@ -0,0 +1,34 @@ +#include "GigLineNPCPerson.h" + +#include "Components/CapsuleComponent.h" +#include "Components/SkeletalMeshComponent.h" +#include "Engine/SkeletalMesh.h" + +AGigLineNPCPerson::AGigLineNPCPerson() +{ + // The MPFB human is ~1.66 m tall, ~0.43 m wide. Half-height 83 cm leaves + // the head top at ~1.66 m, so a 1.60 m camera does not clip it. Radius + // ~35 cm accommodates the relaxed idle pose (arms at the sides). + GetCapsuleComponent()->InitCapsuleSize(35.0f, 83.0f); + GetCapsuleComponent()->SetRelativeLocation(FVector(0.0f, 0.0f, 83.0f)); + + // Visible body: the imported MPFB human on ACharacter's built-in mesh + // component, feet at Z 0 (capsule base lowered to the floor). + USkeletalMeshComponent* Mesh = GetMesh(); + Mesh->SetRelativeLocation(FVector(0.0f, 0.0f, -83.0f)); + Mesh->SetRelativeRotation(FRotator(0.0f, -90.0f, 0.0f)); + Mesh->SetCastShadow(true); + + static ConstructorHelpers::FObjectFinder MeshAsset( + TEXT("/Game/Test/Character/character")); + if (MeshAsset.Succeeded()) + { + Mesh->SetSkeletalMeshAsset(MeshAsset.Object); + Mesh->SetSkeletalMesh(MeshAsset.Object); + } + + // An NPC pawn has no player input; hand it to AI when it is spawned into + // the world (the 0.5.0 pedestrian navigation will drive it). + AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned; + bUseControllerRotationYaw = false; +} \ No newline at end of file diff --git a/Game/GigLine/Source/GigLine/GigLineNPCPerson.h b/Game/GigLine/Source/GigLine/GigLineNPCPerson.h new file mode 100644 index 0000000..06624f5 --- /dev/null +++ b/Game/GigLine/Source/GigLine/GigLineNPCPerson.h @@ -0,0 +1,32 @@ +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Character.h" +#include "GigLineNPCPerson.generated.h" + +/** + * Reusable NPC/pedestrian prefab built on the MPFB human asset. + * + * Deliberately NOT the 0.1.0 player (which is AGigLineCharacter, a capsule + + * camera). This is the visible-body template for future NPCs/pedestrians + * (0.5.0): ACharacter's collision capsule sized to the ~1.66 m MPFB human, + * with the character skeletal mesh on the built-in mesh component as the + * visible body, feet at Z 0. + * + * Head-height guarantee: capsule half-height 83 cm (total 1.66 m), so the head + * top sits at ~1.66 m; a camera/view at 1.60 m sits just below eye level and + * does not clip the head or neck. Set HeadHeightCm after observing the posed + * mesh if the eye socket lands elsewhere. + */ +UCLASS() +class GIGLINE_API AGigLineNPCPerson : public ACharacter +{ + GENERATED_BODY() + +public: + AGigLineNPCPerson(); + + /** Observed head height (cm above Z 0), used to verify camera clearance. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "NPC") + float HeadHeightCm = 155.f; +}; \ No newline at end of file diff --git a/Game/GigLine/Source/GigLine/GigLineShotActor.cpp b/Game/GigLine/Source/GigLine/GigLineShotActor.cpp index ae492cc..290cd4a 100644 --- a/Game/GigLine/Source/GigLine/GigLineShotActor.cpp +++ b/Game/GigLine/Source/GigLine/GigLineShotActor.cpp @@ -27,8 +27,12 @@ void AGigLineShotActor::Tick(float DeltaSeconds) if (SettleCountdown <= 0.f) { bWaitingForSettle = false; - const FString FileName = FString::Printf(TEXT("review-%02d-%s"), CurrentShot, - CurrentShot < ShotNames.Num() ? *ShotNames[CurrentShot] : *FString::FromInt(CurrentShot)); + // Use the stable custom name only (no list-index prefix): inserting or + // reordering cameras must not renumber files and orphan the old ones. + const FString Name = CurrentShot < ShotNames.Num() + ? ShotNames[CurrentShot] + : FString::FromInt(CurrentShot); + const FString FileName = FString::Printf(TEXT("review-%s"), *Name); // Resolution is the game window's; the runner passes -ResX/-ResY. FScreenshotRequest::RequestScreenshot(FileName, false, false); bWaitingForScreenshot = true; diff --git a/ROADMAP.md b/ROADMAP.md index c2a8652..f59349a 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -28,7 +28,7 @@ Workstation, geo pipeline, and engine validation are done. The art pipeline has **Geographic data (complete, with blockout detail open):** the OSM extract, address, footprints, demo bounds, EPSG:26911 frame with a recorded origin, and the 3DEP 1 m DEM are verified and committed. The meter→centimetre conversion lives in one function, and every exporter must call it. Block buildings still need height data for three placeholders and cleaning or detailing of the Blosm blockout. -**Review and character (decision):** the review scene is the staging used for all visual approval. Its scope is the character's ground contact, the camera list, and the façade detail shots, and nothing in asset production or gameplay continues past it until these shots pass. The shot list is in `Screenshots/README.md`. Coordinator decision 2026-11-07: **`0.1.0` ships first-person only**, a capsule with a camera and no visible player body. The MPFB character asset (idle pose, warm skin) is the template for later **NPCs** (`0.2.0` body/camera toggle, `0.5.0` pedestrians). It stays a standalone character prefab, not wired into the `AGigLineCharacter` pawn. +**Review and character (decision):** the review scene is the staging used for all visual approval. Its scope is the character's ground contact, the camera list, and the façade detail shots, and nothing in asset production or gameplay continues past it until these shots pass. The shot list is in `Screenshots/README.md`. Coordinator decision 2026-11-07: **`0.1.0` ships first-person only**, a capsule with a camera and no visible player body. The MPFB character asset (idle pose, warm skin) is the template for later **NPCs** (`0.2.0` body/camera toggle, `0.5.0` pedestrians). It is now a concrete reusable prefab, `AGigLineNPCPerson` (collision capsule + visible body, feet on Z 0), kept standalone and not wired into the `AGigLineCharacter` pawn. **Asset production:** the scale test and the `ue_metric_v1` export and import presets are established. The hero façade first pass is built and screenshot-approved; the north-side buildings (TCL Chinese Theatre, Madame Tussaud's) are staged. Open: reference collection for the door recess and the night frame, the surrounding block frontage, sidewalks, roads and collision, and materials for all assets. diff --git a/Screenshots/README.md b/Screenshots/README.md index 3945c8c..fecc2d2 100644 --- a/Screenshots/README.md +++ b/Screenshots/README.md @@ -27,19 +27,24 @@ and exits — about a minute. The camera list lives in ## Review-map shots -All ☑ approved 2026-11-07 unless noted. Click an image for the full-size PNG. +All ☑ approved 2026-11-07 unless noted. `review-npc-prefab` is new (☐ pending). + +> Naming: shots use their stable `Tools/ue_build_review_map.py` camera name (no +> index prefix), so adding or reordering cameras never renumbers files or +> orphans the old ones. Click an image for the full-size PNG. | File | Preview | View | What it shows | | --- | --- | --- | --- | -| `review-00-overview.png` | [overview](review-00-overview.png) | 7 m up, NE | Whole review layout: character + both cubes on the floor | -| `review-01-character.png` | [character](review-01-character.png) | eye height, south | MPFB mannequin, idle pose + warm skin; feet meet the plane | -| `review-02-scale-cubes.png` | [scale cubes](review-02-scale-cubes.png) | south | Floating vs grounded cube: cast vs contact shadow | -| `review-03-player-placement.png` | [player placement](review-03-player-placement.png) | spawn | The `AGigLineCharacter` pawn, capsule height and landing | -| `review-04-ground-contact.png` | [ground contact](review-04-ground-contact.png) | knee height, feet | Floating/sinking character is obvious here | -| `review-05-shadow-compare.png` | [shadow compare](review-05-shadow-compare.png) | low angle | Detached (floating) vs touching (grounded) shadows | -| `review-06-hero-portals.png` | [hero portals](review-06-hero-portals.png) | 8 m, eye height | Three genuine 1.2 m portal recesses with flights and plinth | -| `review-07-hero-facade-front.png` | [hero facade front](review-07-hero-facade-front.png) | road side, straight-on | Hero façade elevation and column rhythm | -| `review-08-hero-facade-oblique.png` | [hero facade oblique](review-08-hero-facade-oblique.png) | north-west | Body depth and column line standing proud of the mass | +| `review-overview.png` | [overview](review-overview.png) | 7 m up, NE | Whole review layout: character + both cubes on the floor | +| `review-character.png` | [character](review-character.png) | eye height, south | MPFB mannequin, idle pose + warm skin; feet meet the plane | +| `review-scale-cubes.png` | [scale cubes](review-scale-cubes.png) | south | Floating vs grounded cube: cast vs contact shadow | +| `review-player-placement.png` | [player placement](review-player-placement.png) | spawn | The `AGigLineCharacter` pawn, capsule height and landing | +| `review-npc-prefab.png` | [npc prefab](review-npc-prefab.png) | mid, west | The `AGigLineNPCPerson` prefab: collision capsule + visible MPFB body, feet on Z 0 (**new 2026-11-07**) | +| `review-ground-contact.png` | [ground contact](review-ground-contact.png) | knee height, feet | Floating/sinking character is obvious here | +| `review-shadow-compare.png` | [shadow compare](review-shadow-compare.png) | low angle | Detached (floating) vs touching (grounded) shadows | +| `review-hero-portals.png` | [hero portals](review-hero-portals.png) | 8 m, eye height | Three genuine 1.2 m portal recesses with flights and plinth | +| `review-hero-facade-front.png` | [hero facade front](review-hero-facade-front.png) | road side, straight-on | Hero façade elevation and column rhythm | +| `review-hero-facade-oblique.png` | [hero facade oblique](review-hero-facade-oblique.png) | north-west | Body depth and column line standing proud of the mass | ## Demo-map views @@ -47,10 +52,10 @@ Rendered from `/Game/Hollywood/DemoMap`, the walkable level. Click an image for | File | Preview | Approval | What it shows | | --- | --- | --- | --- | -| `demo/review-00-demo-spawn-view.png` | [spawn view](demo/review-00-demo-spawn-view.png) | ☑ | Spawn view: north sidewalk, the hero façade across the road | -| `demo/review-01-demo-street-level.png` | [street level](demo/review-01-demo-street-level.png) | ☑ | Street level: portals, flights, banners, colonnade | -| `demo/review-02-demo-crossing.png` | [crossing](demo/review-02-demo-crossing.png) | needs review | From the west, looking along the block (foreground blocking) | -| `demo/review-03-demo-north-buildings.png` | [north buildings](demo/review-03-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) | ☑ | 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) | needs review | From the west, looking along the block (foreground blocking) | +| `demo/review-demo-north-buildings.png` | [north buildings](demo/review-demo-north-buildings.png) | ☑ | North-side background buildings (TCL, Tussaud's) across the Blvd | ## What to look for diff --git a/Screenshots/demo/review-00-demo-spawn-view.png b/Screenshots/demo/review-00-demo-spawn-view.png deleted file mode 100644 index dc0f647..0000000 --- a/Screenshots/demo/review-00-demo-spawn-view.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0f3d6f0c1885b708645092ad7aeae9b94b7bb1f9df69976886d3055a84877fe9 -size 2599672 diff --git a/Screenshots/demo/review-01-demo-street-level.png b/Screenshots/demo/review-01-demo-street-level.png deleted file mode 100644 index 71736df..0000000 --- a/Screenshots/demo/review-01-demo-street-level.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:75e2814c90d94fad5789aae830d59238544d5360223be9e40ab59cebdc2f09db -size 1926763 diff --git a/Screenshots/demo/review-02-demo-crossing.png b/Screenshots/demo/review-02-demo-crossing.png deleted file mode 100644 index da100be..0000000 --- a/Screenshots/demo/review-02-demo-crossing.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8d304878047072351acddaa27bdd571da4948fb0a5dffa4d901ba6f9e88f2b54 -size 1285673 diff --git a/Screenshots/demo/review-03-demo-north-buildings.png b/Screenshots/demo/review-03-demo-north-buildings.png deleted file mode 100644 index 7f978cd..0000000 --- a/Screenshots/demo/review-03-demo-north-buildings.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e604f6d549f854f4fea2d935ad551111ec0b4f3cfad0219549427814196b0ca6 -size 1777325 diff --git a/Screenshots/demo/review-demo-crossing.png b/Screenshots/demo/review-demo-crossing.png new file mode 100644 index 0000000..408995a --- /dev/null +++ b/Screenshots/demo/review-demo-crossing.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e7791340703cf239fa953504f2b38ce6a96ec131e35e622078acb51d0957e763 +size 1292359 diff --git a/Screenshots/demo/review-demo-north-buildings.png b/Screenshots/demo/review-demo-north-buildings.png new file mode 100644 index 0000000..b39fbc2 --- /dev/null +++ b/Screenshots/demo/review-demo-north-buildings.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:95f580993db879516ee1f4f243b6cd00abad523d10cf23b1cb1a39324365de05 +size 1775921 diff --git a/Screenshots/demo/review-demo-spawn-view.png b/Screenshots/demo/review-demo-spawn-view.png new file mode 100644 index 0000000..c965b34 --- /dev/null +++ b/Screenshots/demo/review-demo-spawn-view.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:788f71de1bf5080c738da6d94170e3d5a577823cd33aa5a10dd892f39b41518f +size 2585447 diff --git a/Screenshots/demo/review-demo-street-level.png b/Screenshots/demo/review-demo-street-level.png new file mode 100644 index 0000000..67951c1 --- /dev/null +++ b/Screenshots/demo/review-demo-street-level.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:292f503ebbd786d87c44ad2a98ef5458bef582134c59c34ae5cfd24331268ba6 +size 1932918 diff --git a/Screenshots/review-00-overview.png b/Screenshots/review-00-overview.png deleted file mode 100644 index b7f641c..0000000 --- a/Screenshots/review-00-overview.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d1505cbc55946c31921d720aa37d9611306f8f224f312bcaa44c9be20fb51f8f -size 2431032 diff --git a/Screenshots/review-01-character.png b/Screenshots/review-01-character.png deleted file mode 100644 index c6cf80d..0000000 --- a/Screenshots/review-01-character.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b561fc09da1d609f36627d7253ee57392709365d87e991e0478e94aa25351729 -size 2474855 diff --git a/Screenshots/review-02-scale-cubes.png b/Screenshots/review-02-scale-cubes.png deleted file mode 100644 index 69ced52..0000000 --- a/Screenshots/review-02-scale-cubes.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:23a344b379225dbb047dd06199a679b7bc28694ceba34b1985253954fd488c07 -size 1909809 diff --git a/Screenshots/review-03-player-placement.png b/Screenshots/review-03-player-placement.png deleted file mode 100644 index 5a0eb52..0000000 --- a/Screenshots/review-03-player-placement.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9d94797297707283f7c541cd7191bf33dc1e3def84dd5562204041c971ffb090 -size 1964632 diff --git a/Screenshots/review-04-ground-contact.png b/Screenshots/review-04-ground-contact.png deleted file mode 100644 index 89886d5..0000000 --- a/Screenshots/review-04-ground-contact.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c7d8ef949f148f1f076c36377ffd606f83dacec44423ce901337b8ac83f9516f -size 1839543 diff --git a/Screenshots/review-05-shadow-compare.png b/Screenshots/review-05-shadow-compare.png deleted file mode 100644 index d348422..0000000 --- a/Screenshots/review-05-shadow-compare.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:306ec54dab6ed6498fc401024a57ada32903baaa357b62d033911db8c3fdd64a -size 1672855 diff --git a/Screenshots/review-06-hero-portals.png b/Screenshots/review-06-hero-portals.png deleted file mode 100644 index 0794217..0000000 --- a/Screenshots/review-06-hero-portals.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6ab647c7cce9db83a79875b8eaa42205f4468ff957a59c67e90fb4b4e133ddd3 -size 1752976 diff --git a/Screenshots/review-07-hero-facade-front.png b/Screenshots/review-07-hero-facade-front.png deleted file mode 100644 index 45cb56f..0000000 --- a/Screenshots/review-07-hero-facade-front.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ccf12d909e095f113f655df87efa3bf27f5fe05a547e5647590ecb3bcbbc27a7 -size 2048644 diff --git a/Screenshots/review-08-hero-facade-oblique.png b/Screenshots/review-08-hero-facade-oblique.png deleted file mode 100644 index 5f87c2c..0000000 --- a/Screenshots/review-08-hero-facade-oblique.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6cf0d70b3a5e8ea97e1f77b56b38e4f73a47b909c2bdf4bf64cd981d998ed3bf -size 1924852 diff --git a/Screenshots/review-character.png b/Screenshots/review-character.png new file mode 100644 index 0000000..dfb05f8 --- /dev/null +++ b/Screenshots/review-character.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4f7e7731e6228ba7954947545a02066e415989a8bf2aab96c4113891a78221d7 +size 2202483 diff --git a/Screenshots/review-ground-contact.png b/Screenshots/review-ground-contact.png new file mode 100644 index 0000000..516451f --- /dev/null +++ b/Screenshots/review-ground-contact.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3c20c78e6c9a109dea7e0aafd0ec9cfe799ca5cf5961398c3a369481ac675af2 +size 1705468 diff --git a/Screenshots/review-hero-facade-front.png b/Screenshots/review-hero-facade-front.png new file mode 100644 index 0000000..e267105 --- /dev/null +++ b/Screenshots/review-hero-facade-front.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3369c4eccf8902a9e84056b0fe419735b29983d08ae26ba112374cf601c15df0 +size 2304244 diff --git a/Screenshots/review-hero-facade-oblique.png b/Screenshots/review-hero-facade-oblique.png new file mode 100644 index 0000000..3a7cee3 --- /dev/null +++ b/Screenshots/review-hero-facade-oblique.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a77f2f03eee7a351713937387cdb2dafe590e607f3960c18c31d600d2f9c7d47 +size 2404004 diff --git a/Screenshots/review-hero-portals.png b/Screenshots/review-hero-portals.png new file mode 100644 index 0000000..5db95d1 --- /dev/null +++ b/Screenshots/review-hero-portals.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5c65c6f974a5ef490a0da4a36fd8d237211d3efa0540040eb0ef3bca4a7d137 +size 1794383 diff --git a/Screenshots/review-npc-prefab.png b/Screenshots/review-npc-prefab.png new file mode 100644 index 0000000..42230dd --- /dev/null +++ b/Screenshots/review-npc-prefab.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1a52cf51af9cfb70e788c0e6d81cb33737380bc8c3adb4f72a9c8c2ef9aa74f5 +size 1660515 diff --git a/Screenshots/review-overview.png b/Screenshots/review-overview.png new file mode 100644 index 0000000..9a2acc1 --- /dev/null +++ b/Screenshots/review-overview.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:068f9cd7209a02f9e6e1f6c6babfefa43507992600eb7086fbea9792367bb379 +size 2127805 diff --git a/Screenshots/review-player-placement.png b/Screenshots/review-player-placement.png new file mode 100644 index 0000000..a256838 --- /dev/null +++ b/Screenshots/review-player-placement.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b766e74af88547fe201766bc118bbea70b478e9f7ff9f9ac4251343bd39fb8e +size 1847345 diff --git a/Screenshots/review-scale-cubes.png b/Screenshots/review-scale-cubes.png new file mode 100644 index 0000000..f6cf6ae --- /dev/null +++ b/Screenshots/review-scale-cubes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0c2a93e30216c76e7db5ad28ce9b0e580cfcd8c269b1494cdc65b6ce57b788a +size 2066648 diff --git a/Screenshots/review-shadow-compare.png b/Screenshots/review-shadow-compare.png new file mode 100644 index 0000000..cb38caa --- /dev/null +++ b/Screenshots/review-shadow-compare.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e708d4d8b765ec270b57af26a1134ccd7fcc34d55123634599a316d8dc9d4327 +size 2052859 diff --git a/TODO.md b/TODO.md index 784e07f..ef1f9e2 100644 --- a/TODO.md +++ b/TODO.md @@ -61,11 +61,11 @@ Owner: this session. Scope: the review scene used for all visual approval — th - [x] Put the character on the ground rather than floating. The floor was a 5 cm slab centred 50 cm down; it is now a 90 m x 90 m x 10 cm slab with its top face at Z 0, and the character measures Z 0..83 cm — feet on the plane (`Screenshots/README.md`). - [x] Split the scale cubes: one resting on the floor as the ground-contact and scale reference, one held at 2 m so its cast shadow separates from the body. - [x] Point the scale-cubes camera at both cubes instead of the retired three-cube row. -- [x] **Character placement shot**: `review-03-player-placement` shows the pawn (`AGigLineCharacter`) staged by `Tools/ue_build_review_map.py` with a cube behind it. Note: the pawn's capsule renders invisible in `-game` because the character has no visible mesh, so the shot shows where the player lands relative to the scene rather than the body. -- [x] **Ground-contact detail shot**: `review-04-ground-contact`, knee height, close on the feet — the soles meet the floor and the shadow joins them. -- [x] **Shadow-comparison detail shot**: `review-05-shadow-compare`, both cubes in one frame from a low angle; the floating cube's shadow is detached, the grounded one's touches. -- [x] **Detail shot of the hero façade portals**: `review-06-hero-portals` from 8 m at eye height now fills the frame with all three doorways. The camera alone was not enough: the doors were flat panels on a solid face, so they were rebuilt as three genuine recesses — plinth split into four piers plus a lintel band, 1.2 m reveal, stone jambs and lintels standing 0.10 m proud, three mullions each. Facade bounds are now exactly Z 0..14.50 m; a jamb overhang had been sinking the model 0.15 m into the ground. -- [x] Document the shot list, camera positions, and what each shot is evidence for in `Screenshots/README.md`; nine shots, each with an approval line. +- [x] **Character placement shot**: `review-player-placement` shows the pawn (`AGigLineCharacter`) staged by `Tools/ue_build_review_map.py` with a cube behind it. Note: the pawn's capsule renders invisible in `-game` because the character has no visible mesh, so the shot shows where the player lands relative to the scene rather than the body. +- [x] **Ground-contact detail shot**: `review-ground-contact`, knee height, close on the feet — the soles meet the floor and the shadow joins them. +- [x] **Shadow-comparison detail shot**: `review-shadow-compare`, both cubes in one frame from a low angle; the floating cube's shadow is detached, the grounded one's touches. +- [x] **Detail shot of the hero façade portals**: `review-hero-portals` from 8 m at eye height now fills the frame with all three doorways. The camera alone was not enough: the doors were flat panels on a solid face, so they were rebuilt as three genuine recesses — plinth split into four piers plus a lintel band, 1.2 m reveal, stone jambs and lintels standing 0.10 m proud, three mullions each. Facade bounds are now exactly Z 0..14.50 m; a jamb overhang had been sinking the model 0.15 m into the ground. +- [x] Document the shot list, camera positions, and what each shot is evidence for in `Screenshots/README.md`; shots use stable names with an approval line (10 review + 4 demo). - [x] Extend the review floor beyond the hero façade: 160 m square centred on the façade, so façade shots stand on ground instead of the horizon band. - [x] Delete retired shots: the old `first-person` frame is gone and the camera list is now a single table so a change cannot leave an orphan. @@ -75,14 +75,15 @@ The MPFB character asset is planned as the NPC template (see `ROADMAP.md`). Keep - [x] Give the MPFB character asset a standing idle pose so review and NPC shots show a person, not an A-pose bind. Done: `Tools/mpfb_character_export.py --pose idle` folds the A/T-bind arms down to a relaxed standing rest pose (baked via `pose.armature_apply` before export; `upperarm01.L` rest z −0.71 → −1.91 rad), re-exported, re-imported (138 bones), rendered in `review-01-character`. - [x] Bake a second skin tone or clothing layer, so the character does not read as a grey mannequin in shots. Done: the v2-skin bake came out **black** and slow at 1024², so we synthesize a warm skin-tone diffuse directly (`Tools/mpfb_character_export.py` `make_diffuse`, with a darker shorts-suggestive bottom band) via a stdlib/numpy PNG writer. Character reads warm, not grey. Screenshot approved (`Screenshots/review-01-character`). -- [ ] (open) Keep the character as a reusable NPC prefab: a visible body, a collision capsule, and a head-height check that the camera does not clip at 1.60 m — for when NPCs/pedestrians are wired in (`0.2.0+`), not the `0.1.0` player. +- [x] Keep the character as a reusable NPC prefab: a visible body, a collision capsule, and a head-height check that the camera does not clip at 1.60 m — for when NPCs/pedestrians are wired in (`0.2.0+`), not the `0.1.0` player. Done 2026-11-07: `AGigLineNPCPerson` (C++), ACharacter with capsule half-height 83 cm / total 1.66 m (head at ~1.66 m, so a 1.60 m camera does not clip), the MPFB human on the built-in mesh as the visible body, `AutoPossessAI` for pedestrians; confirmed spawned feet-on-Z-0 and rendered in `Screenshots/review-npc-prefab` (☐ pending approval). +- [x] Screenshots use stable camera names (no index prefix), so inserting a shot no longer renumbers/ orphans files; `AGigLineShotActor` + `Screenshots/README.md` updated. ### Asset production - [x] Establish a consistent scale test, named export presets, and repeatable import settings. Scale test = the three-cube calibration scene (`Art/blender/scaletest/`, regenerable via `Tools/scale_test_export.py`); named export preset `ue_metric_v1` (FBX settings in that script); named import preset `ue_metric_v1` (`FbxImportUI` settings in `Tools/ue_scale_test_import.py`). Both scripts are the templates for production asset export/import. - [-] Collect reference photography for manual façade work. Local only, never committed: `Art/reference/` is gitignored. **72 images in eleven folders**: hero façades to 4k, the El Capitan Theatre exterior series, premiere-night staging, Ovation Hollywood, the north side, and the Lee Drug neighbour. Gaps largely closed by the Flickr/web-search pass: the hero entrance is now documented (three portals with individual short flights — the inherited "eight steps, 12 m wide" is overturned), and the 6806–6822 retail run appears in an oblique 2013 frame. Still missing: a clean isolated frame of the door recess and a night frame of 6840 itself. - [x] Download reference photography for the two north-side buildings inside the 50 m buffer — TCL Chinese Theatre (OSM way 424105908, 6925 Hollywood Blvd) and Madame Tussaud's (way 424105889, 6933 Hollywood Blvd). Done 2026-11-06 with `Tools/commons_fetch.py` against the Wikimedia Commons API: eleven frames in the local `Art/reference/north-chinese/` and `Art/reference/north-tussauds/`, provenance in each `sidecar.json`, nothing committed. -- [x] Model hero façade for the El Capitan Entertainment Centre site. **Second pass built 2026-11-07** (proportions recalibrated from the 2009 photograph: entablature 3.4 m, parapet 2.2 m, architrave and cornice bands, square pedestals — the first pass had 5.7 m of blank wall above the capitals and read as a warehouse). **First pass built 2026-11-07** (`Tools/hero_facade_build.py` → `Art/blender/hero/hero-facade-2026-11-07.blend` + JSON part report with evidence per part → `Tools/ue_hero_facade_import.py` → `/Game/Hollywood/ElCapitan/SM_HeroFacade`, 1892 tris, 4 materials, placed on the measured building line). Six columns at 4.96 m over the 25.75 m frontage, plinth, three portal flights (5 risers, not the old 8), entablature 2.2 m, parapet 1.6 m with motto band and medallions. **Screenshot approved** (`Screenshots/review-06` portals, `review-07/08` facade). Next pass: banner and window placement, then materials. +- [x] Model hero façade for the El Capitan Entertainment Centre site. **Second pass built 2026-11-07** (proportions recalibrated from the 2009 photograph: entablature 3.4 m, parapet 2.2 m, architrave and cornice bands, square pedestals — the first pass had 5.7 m of blank wall above the capitals and read as a warehouse). **First pass built 2026-11-07** (`Tools/hero_facade_build.py` → `Art/blender/hero/hero-facade-2026-11-07.blend` + JSON part report with evidence per part → `Tools/ue_hero_facade_import.py` → `/Game/Hollywood/ElCapitan/SM_HeroFacade`, 1892 tris, 4 materials, placed on the measured building line). Six columns at 4.96 m over the 25.75 m frontage, plinth, three portal flights (5 risers, not the old 8), entablature 2.2 m, parapet 1.6 m with motto band and medallions. **Screenshot approved** (`Screenshots/review-hero-portals`, `review-hero-facade-front/oblique`). Next pass: banner and window placement, then materials. - [ ] Model surrounding block frontage. - [x] Model simplified background buildings directly across Hollywood Boulevard. Done 2026-11-07, the two in-scope north-side buildings — TCL Chinese Theatre (SM_TCL_Chinese_Theatre) and Madame Tussaud's (SM_Madame_Tussauds) — taken from the verified OSM blockout volumes and exported with `ue_metric_v1`, with a Y-mirror so they land north of Hollywood Blvd across the street from the hero (TCL spans Unreal Y 1802..9431, Tussaud's 1888..7586; road centreline ≈ 0, hero façade at Y −1450). Staged in the DemoMap as `North_TCL_Chinese` / `North_Madame_Tussauds`; demo shot `review-03-demo-north-buildings` captured. Simple flat facade materials (surfaces are the `0.1.4` patch). - [-] Build sidewalks, roads, and collision meshes. Terrain, road, sidewalks and kerbs exist (`Art/blender/terrain/`, `street/`) and are assembled in `site/site-2026-11-06.blend`; collision and details (crossing markings, kerb radii, aprons) are not built. Ground must follow the DEM: 3.94 m fall along the block, ~2 % crown between the gutters at ±10–11 m from the centreline, north sidewalk higher than south. Kerb height is an unresolved assumption — the DEM does not resolve it (`Geo/verification/2026-11-06-cross-sections.md`). @@ -174,7 +175,7 @@ Suggested blender skills: **procedural-modeling** + **geometry-nodes** (the faç - [x] Generate MPFB human base mesh; export baked materials and deform skeleton. Done in bootstrap: idle-pose 19 158-vert human with a 137-bone standard rig, `Tools/mpfb_character_export.py` (now `--pose idle` + warm `make_diffuse`). - [x] Import character into Unreal; validate skeleton, scale, and materials. Done: `/Game/Test/Character` — `SkeletalMesh` (138 bones), `Skeleton`, warm `M_Character_Export` material with baked `T_Character_Diffuse`. -- [ ] Promote the character asset into a reusable NPC prefab (visible body, collision capsule, idle pose) ready for pedestrians (`0.5.0`) once the third-person body lands. +- [x] Promote the character asset into a reusable NPC prefab (visible body, collision capsule, idle pose) ready for pedestrians (`0.5.0`) once the third-person body lands. Done 2026-11-07: `AGigLineNPCPerson` (see the 0.1.0 Character-development item); `0.5.0` reuses it as the pedestrian base. - [ ] Create basic idle/walk animation set. - [ ] Verify animation plays correctly on the imported rig. @@ -293,7 +294,7 @@ Suggested blender skills: **procedural-modeling** + **geometry-nodes** (the faç ### Simulation -- [ ] Add pedestrian characters with navigation, reusing the idle-pose character asset as the NPC prefab (`/Game/Test/Character`). +- [ ] Add pedestrian characters with navigation, using `AGigLineNPCPerson` (the reusable NPC prefab) as the base, driving it via AI/`AutoPossessAI`. - [ ] Implement crosswalk behavior (pedestrians use the designated crossing). - [ ] Add limited ambient traffic on the street network. - [ ] Optimize simulation cost to stay within frame budget. diff --git a/Tools/ue_build_review_map.py b/Tools/ue_build_review_map.py index 475846d..168fc5b 100644 --- a/Tools/ue_build_review_map.py +++ b/Tools/ue_build_review_map.py @@ -25,6 +25,20 @@ def log(msg): lines.append(str(msg)) +def ensure_writable(path): + # A packed .umap left read-only (git sets 644->444 on checkout, or the + # editor re-flags it) makes save_map silently return False with no error. + # chmod u+w it so the staged camera list and actors actually persist. + p = os.path.join(unreal.Paths.project_dir(), path.replace("/Game/", "Content/")) + try: + mode = os.stat(p).st_mode & 0o777 + if mode & 0o200 == 0: + os.chmod(p, 0o644) + log(f"map writable: chmod u+w {p} (was {oct(mode)})") + except FileNotFoundError: + pass + + def spawn(cls, loc, label=None, scale=None, rot=None): a = unreal.EditorLevelLibrary.spawn_actor_from_class(cls, loc) if a is None: @@ -149,6 +163,18 @@ try: else: log("MISSING: /Script/GigLine.GigLineCharacter") + # --- 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. + npc = None + npc_class = unreal.load_class(None, "/Script/GigLine.GigLineNPCPerson") + if npc_class: + npc = spawn(npc_class, unreal.Vector(-4500, 300, 0), "NPC_Person") + if npc: + log("staged: NPC_Person (AGigLineNPCPerson) at Z 0") + else: + log("MISSING: /Script/GigLine.GigLineNPCPerson") + # --- camera list --------------------------------------------------------- # Every camera is a (name, location, target) triple; the look-at rotation is # computed from the pair so a camera cannot drift out of aim when one number @@ -162,6 +188,9 @@ try: ("scale-cubes", unreal.Vector(0, -900, 200), unreal.Vector(0, 600, 80)), # the pawn the game spawns, with the grounded cube beside it ("player-placement", unreal.Vector(320, -520, 140), unreal.Vector(0, 0, 80)), + # the NPC prefab: capsule + visible body, feet on Z 0, at double scale + # distance because it stands off to the west of the main staging + ("npc-prefab", unreal.Vector(-4200, 600, 160), unreal.Vector(-4500, 300, 90)), # low, close on feet and cube base: ground contact must be obvious ("ground-contact", unreal.Vector(180, -260, 35), unreal.Vector(0, 40, 20)), # shadow comparison, low angle so both shadows read @@ -214,6 +243,7 @@ try: # quit after the demo shots so a dedicated DemoMap capture run # terminates cleanly (render_screenshots.sh runs both maps). seq.set_editor_property("quit_when_done", True) + ensure_writable("/Game/Hollywood/DemoMap") saved = unreal.EditorLoadingAndSavingUtils.save_map( unreal.EditorLevelLibrary.get_editor_world(), "/Game/Hollywood/DemoMap") log(f"demo map staged: {len(demo_shots)} cameras, save {saved}") @@ -239,6 +269,7 @@ except Exception as exc: # noqa # --- 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}") From bc2e421d330eced26784eb711303cfa7c6353958 Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Sat, 7 Nov 2026 15:46:45 +0100 Subject: [PATCH 4/8] =?UTF-8?q?gameplay:=20designated=20street=20crossing?= =?UTF-8?q?=20=E2=80=94=20zebra=20markings,=20corridor=20cut,=20up-facing?= =?UTF-8?q?=20street?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - street_mesh.py: continental zebra markings (0.5 m bars, 0.5 m gaps, 3 m band) at all three signalised crossings, triangles not quads (FBX SDK refuses to triangulate the crown-twisted quads), stripes +6 cm above the DEM so they stay above the road surface; winding flipped so every street surface faces up (road/sidewalks/markings were 100% down-facing and invisible in engine once the terrain stopped covering them). - terrain_from_dem.py: cut a 10.7 m corridor around the Hollywood Blvd centreline so road, kerbs and sidewalks are not buried under the terrain; sidewalks themselves cover the transition band, walk-check stays at 0 holes. - Blender builders save with a writability guard + fatal failure: *.blend is LFS-lockable and checks out read-only; a failed save previously left downstream steps building from stale files. - ue_import_site.py: Crossing_Markings imports with no collision (paint on the road); ue_build_demo_map.py stages it and chmods the lockable .umap before saving (save_map silently returned False on read-only maps). - ue_build_review_map.py: demo-crossing camera moved to the real crossing (s=130.1 m -> UE -24291,-1340) and fixed the ensure_writable path (was missing the .umap suffix, so the chmod never fired). --- Art/blender/site/export_site_fbx.py | 2 +- Art/blender/site/site-2026-11-06.blend | 4 +- Art/blender/site/site_assembly.py | 10 ++++- Art/blender/street/street-2026-11-06.blend | 4 +- Art/blender/street/street_mesh.py | 32 +++++++++++--- Art/blender/terrain/terrain-2026-11-06.blend | 4 +- Art/blender/terrain/terrain_from_dem.py | 43 ++++++++++++++++++- Game/GigLine/Content/Hollywood/DemoMap.umap | 2 +- .../Hollywood/Site/Crossing_Markings.uasset | 3 ++ .../Content/Hollywood/Site/Kerb_North.uasset | 4 +- .../Content/Hollywood/Site/Kerb_South.uasset | 4 +- .../Hollywood/Site/Road_Hollywood.uasset | 4 +- .../Hollywood/Site/Road_Paint_White.uasset | 3 ++ .../Hollywood/Site/Sidewalk_North.uasset | 4 +- .../Hollywood/Site/Sidewalk_South.uasset | 4 +- .../Content/Hollywood/Site/Terrain_DEM.uasset | 4 +- Game/GigLine/Content/Test/ReviewMap.umap | 2 +- Tools/ue_build_demo_map.py | 10 ++++- Tools/ue_build_review_map.py | 4 +- Tools/ue_import_site.py | 22 ++++++++-- 20 files changed, 133 insertions(+), 36 deletions(-) create mode 100644 Game/GigLine/Content/Hollywood/Site/Crossing_Markings.uasset create mode 100644 Game/GigLine/Content/Hollywood/Site/Road_Paint_White.uasset diff --git a/Art/blender/site/export_site_fbx.py b/Art/blender/site/export_site_fbx.py index ae45521..7680e32 100644 --- a/Art/blender/site/export_site_fbx.py +++ b/Art/blender/site/export_site_fbx.py @@ -17,7 +17,7 @@ import sys import bpy MESHES = ("Terrain_DEM", "Road_Hollywood", "Sidewalk_South", "Sidewalk_North", - "Kerb_South", "Kerb_North") + "Kerb_South", "Kerb_North", "Crossing_Markings") def main(): diff --git a/Art/blender/site/site-2026-11-06.blend b/Art/blender/site/site-2026-11-06.blend index dfd337f..83aa233 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:6841f07068fc01c5f02aa66f81657aadb772acfd34739ac8211da867c3618f33 -size 1025033 +oid sha256:7d6d303913de1a2c24b3e04ceb6c43a84a46355470b14ebab6acf146f7dbb636 +size 991800 diff --git a/Art/blender/site/site_assembly.py b/Art/blender/site/site_assembly.py index ef73632..1069424 100644 --- a/Art/blender/site/site_assembly.py +++ b/Art/blender/site/site_assembly.py @@ -63,6 +63,7 @@ APPEND = { "Art/blender/terrain/terrain-2026-11-06.blend": ["Terrain_DEM"], "Art/blender/street/street-2026-11-06.blend": [ "Road_Hollywood", "Sidewalk_South", "Sidewalk_North", "Kerb_South", "Kerb_North", + "Crossing_Markings", ], } BLOCKOUT = "Art/blender/blockout/blockout-2026-11-06.blend" @@ -193,7 +194,14 @@ def main(): out = os.path.abspath(args.out) os.makedirs(os.path.dirname(out), exist_ok=True) - bpy.ops.wm.save_as_mainfile(filepath=out) + # *.blend is LFS-lockable, so a fresh checkout marks it read-only. Make it + # writable again or the save fails and downstream steps build from a stale + # file — which is worse than failing here. + if os.path.exists(out) and not os.access(out, os.W_OK): + os.chmod(out, 0o644) + result = bpy.ops.wm.save_as_mainfile(filepath=out) + if result != {"FINISHED"}: + raise RuntimeError(f"could not save {out}: {result}") print(f"saved {out}") diff --git a/Art/blender/street/street-2026-11-06.blend b/Art/blender/street/street-2026-11-06.blend index f186e53..71028dc 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:8ae571a174d7c07f01ebd90d60ef4c3ffaacc11d0fc8476c6d85eb00acb8fbbd -size 512324 +oid sha256:b6f546fcf86cd08a5e37571a7f42b2e6615925f71f7ca5ed850e801313591f70 +size 514043 diff --git a/Art/blender/street/street_mesh.py b/Art/blender/street/street_mesh.py index cdd99e9..201b616 100644 --- a/Art/blender/street/street_mesh.py +++ b/Art/blender/street/street_mesh.py @@ -148,7 +148,10 @@ def strip(rows, offsets, z_at, name, material): for r in range(len(rows) - 1): for c in range(cols - 1): i = r * cols + c - faces.append((i, i + 1, i + cols + 1, i + cols)) + # Winding faces up: the natural (i, i+1, i+cols+1, i+cols) order + # produces downward normals (every surface faced the ground and was + # invisible in engine until the terrain corridor cut exposed it). + faces.append((i, i + cols, i + cols + 1, i + 1)) mesh = bpy.data.meshes.new(name) mesh.from_pydata(verts, [], faces) mesh.update() @@ -239,7 +242,7 @@ def main(): verts.extend([(x, y, z_road), (x, y, z_walk)]) for idx in range(len(rows) - 1): i = idx * 2 - faces.append((i, i + 2, i + 3, i + 1)) + faces.append((i, i + 1, i + 3, i + 2)) mesh = bpy.data.meshes.new(name) mesh.from_pydata(verts, [], faces) mesh.update() @@ -259,24 +262,34 @@ def main(): paint.diffuse_color = (0.85, 0.85, 0.85, 1.0) def 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 - zone / 2 + k * (w + g) + s0 = s_c - covered / 2 + k * (w + g) corners = [] + base_idx = len(verts) # verts grows only after the stripe is sampled for s_edge in (s0, s0 + w): base, normal, _ = at(s_edge) for off in span: x = base[0] + off * normal[0] y = base[1] + off * normal[1] - corners.append((len(verts), (x, y, (dem_z(x, y) or 0.0) + 0.03))) + # +6 cm above the DEM = 4 cm above the road surface: the road + # 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[1], idx[3], idx[2])) + 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() @@ -303,7 +316,14 @@ def main(): out = os.path.abspath(args.out) os.makedirs(os.path.dirname(out), exist_ok=True) - bpy.ops.wm.save_as_mainfile(filepath=out) + # *.blend is LFS-lockable, so a fresh checkout marks it read-only. Make it + # writable again or the save fails and downstream steps build from a stale + # file — which is worse than failing here. + if os.path.exists(out) and not os.access(out, os.W_OK): + os.chmod(out, 0o644) + result = bpy.ops.wm.save_as_mainfile(filepath=out) + if result != {"FINISHED"}: + raise RuntimeError(f"could not save {out}: {result}") print(f"saved {out}") diff --git a/Art/blender/terrain/terrain-2026-11-06.blend b/Art/blender/terrain/terrain-2026-11-06.blend index ef78caf..c294b9b 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:6f3522d20fd25df5e533cb821117b0237da9a38338b4dfb00e1ad391870100aa -size 470245 +oid sha256:9707f1256667ce156f8104cf84c461a1ee074a2a62c932fd377c9bb39e8f41d8 +size 431130 diff --git a/Art/blender/terrain/terrain_from_dem.py b/Art/blender/terrain/terrain_from_dem.py index a291f02..aa007cf 100644 --- a/Art/blender/terrain/terrain_from_dem.py +++ b/Art/blender/terrain/terrain_from_dem.py @@ -53,6 +53,9 @@ def parse_args(): ap.add_argument("--ymax", type=float, default=110.0) ap.add_argument("--osm", default=None, help="OSM extract; building outlines are cut out so rooftops never become terrain") + ap.add_argument("--corridor", type=float, default=10.7, + help="half-width in m of the Hollywood Blvd corridor to cut out so the " + "street meshes (road, kerbs, sidewalks) are not buried under the terrain") return ap.parse_args(argv) @@ -110,6 +113,30 @@ def pts_flat(ring): return ring +def centreline_polyline(osm_path): + """Hollywood Blvd centreline as a local-frame polyline (Highland -> Orange).""" + nodes, ways = oc.load(osm_path) + line, _, _, _, _ = oc.block_extent( + nodes, ways, "Hollywood Boulevard", "North Orange Drive", "North Highland Avenue") + utm = project_to_utm(line, find_proj()) + return [(e - ORIGIN_E, n - ORIGIN_N) for e, n in utm] + + +def dist_to_polyline(x, y, pts): + best = float("inf") + for (ax, ay), (bx, by) in zip(pts, pts[1:]): + dx, dy = bx - ax, by - ay + l2 = dx * dx + dy * dy + if l2 == 0.0: + continue + t = max(0.0, min(1.0, ((x - ax) * dx + (y - ay) * dy) / l2)) + px, py = ax + t * dx, ay + t * dy + d = (x - px) ** 2 + (y - py) ** 2 + if d < best: + best = d + return best ** 0.5 + + def inside(x, y, ring): """Ray casting point-in-polygon.""" hit = False @@ -146,9 +173,12 @@ def main(): f"X {args.xmin}..{args.xmax}, Y {args.ymin}..{args.ymax}") polys = [] + corridor = None if args.osm: polys = building_polygons(args.osm) print(f"masking {len(polys)} building footprints") + corridor = centreline_polyline(args.osm) + print(f"cutting {args.corridor} m corridor around the Hollywood Blvd centreline") verts = [] ok = [] @@ -168,6 +198,10 @@ def main(): verts.append((x, y, 0.0)) ok.append(False) continue + if corridor is not None and dist_to_polyline(x, y, corridor) <= args.corridor: + verts.append((x, y, 0.0)) + ok.append(False) + continue z = values[ri * width + ci] if nodata is not None and z == nodata: verts.append((x, y, 0.0)) @@ -212,7 +246,14 @@ def main(): out = os.path.abspath(args.out) os.makedirs(os.path.dirname(out), exist_ok=True) - bpy.ops.wm.save_as_mainfile(filepath=out) + # *.blend is LFS-lockable, so a fresh checkout marks it read-only. Make it + # writable again or the save fails and downstream steps build from a stale + # file — which is worse than failing here. + if os.path.exists(out) and not os.access(out, os.W_OK): + os.chmod(out, 0o644) + result = bpy.ops.wm.save_as_mainfile(filepath=out) + if result != {"FINISHED"}: + raise RuntimeError(f"could not save {out}: {result}") print(f"saved {out}") diff --git a/Game/GigLine/Content/Hollywood/DemoMap.umap b/Game/GigLine/Content/Hollywood/DemoMap.umap index edb6e47..5230027 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:d6eb86a071caaa75dd40d5f39e0746a1905844c3e479eb86c25081536f1acab9 +oid sha256:68526b7095cfca222b500ff6968eb687ad1771cb31d0a58cbb7dace518968210 size 32372 diff --git a/Game/GigLine/Content/Hollywood/Site/Crossing_Markings.uasset b/Game/GigLine/Content/Hollywood/Site/Crossing_Markings.uasset new file mode 100644 index 0000000..b85f6c7 --- /dev/null +++ b/Game/GigLine/Content/Hollywood/Site/Crossing_Markings.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27a75622ca7ea90976cd88677961ca6d195b61e965a0ff17d6c8909b012918af +size 74929 diff --git a/Game/GigLine/Content/Hollywood/Site/Kerb_North.uasset b/Game/GigLine/Content/Hollywood/Site/Kerb_North.uasset index 04cc0b0..10a7d05 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:1a4b35c70ec74ff686a5d143719f200ad2f64325207de1a16a901303346850bb -size 83455 +oid sha256:ddadef2aaa688c7525f87d4498949ea64bf9f0e463bc0118bd86a3989a2f5acd +size 83550 diff --git a/Game/GigLine/Content/Hollywood/Site/Kerb_South.uasset b/Game/GigLine/Content/Hollywood/Site/Kerb_South.uasset index 1a2cb71..3a001b0 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:bdbb51730eb458843baedfa9843f13698a0e9461de8590500fb8f4e47ede4509 -size 83678 +oid sha256:8e13d22b4ae7ed978c6755adfd5b8c02d98157177976f792794c083b95797270 +size 83797 diff --git a/Game/GigLine/Content/Hollywood/Site/Road_Hollywood.uasset b/Game/GigLine/Content/Hollywood/Site/Road_Hollywood.uasset index e0c8205..3809334 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:c75e97920a8398f4c68a3b77d82aa9672830ceb0eacf24c73d3d7581119c12fc -size 297476 +oid sha256:20165ed97f13fb5fba8391ca8735d9cf965eb97d676282d549afafb1a274b0ca +size 309810 diff --git a/Game/GigLine/Content/Hollywood/Site/Road_Paint_White.uasset b/Game/GigLine/Content/Hollywood/Site/Road_Paint_White.uasset new file mode 100644 index 0000000..9326644 --- /dev/null +++ b/Game/GigLine/Content/Hollywood/Site/Road_Paint_White.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9cea14b068c15403500252bab7a62e563977691f849904cbff9481572d9121fb +size 56677 diff --git a/Game/GigLine/Content/Hollywood/Site/Sidewalk_North.uasset b/Game/GigLine/Content/Hollywood/Site/Sidewalk_North.uasset index c944bbf..0ad1590 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:64c4d6b5defea6ba10d6dd727592c116d1384286c6d15a657c9ea66def41fd3e -size 145340 +oid sha256:e32a1f9590b943a40f05efbd531ec4e1a6c6933c579a5dec4a3dca8753e408c0 +size 150676 diff --git a/Game/GigLine/Content/Hollywood/Site/Sidewalk_South.uasset b/Game/GigLine/Content/Hollywood/Site/Sidewalk_South.uasset index 00af4e0..5b0956e 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:1a7ac89ca1b3e1c6ff8b6b383b60b3e356808d84ce43eae2a1f2280498266a86 -size 161213 +oid sha256:d66130107a5d5d996d8367cc1cbb55c7ef506a91df419f0c9ad5ee74d652b915 +size 164347 diff --git a/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset b/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset index 49d4b5d..bd1a338 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:f2903b135e88499fe9af6d579c037f5b0b7560c229349b832411d70fd9198238 -size 317540 +oid sha256:6d5d3a6fb6646ebeef426b7c07a58b872060779e6ffe0c8b073939ee93299ab8 +size 333138 diff --git a/Game/GigLine/Content/Test/ReviewMap.umap b/Game/GigLine/Content/Test/ReviewMap.umap index 461473e..816d9a2 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:4d2d5e8d826561523a076f0b6e11ab53d46d744b024311281b39a454f96f8f0c +oid sha256:208a1670a337ad19c75ee4b58bfd997dc6018f770ea9edaf85680f6bf723f10c size 32736 diff --git a/Tools/ue_build_demo_map.py b/Tools/ue_build_demo_map.py index f02b97f..8e6a6a7 100644 --- a/Tools/ue_build_demo_map.py +++ b/Tools/ue_build_demo_map.py @@ -15,6 +15,8 @@ # Every actor is (re)created by label, so rerunning the script rebuilds the map # deterministically instead of stacking duplicates. +import os + import unreal MAP = "/Game/Hollywood/DemoMap" @@ -83,7 +85,7 @@ def build(): # --- site meshes, if they have been imported -------------------------- imported = 0 for name in ("Terrain_DEM", "Road_Hollywood", "Sidewalk_South", "Sidewalk_North", - "Kerb_South", "Kerb_North"): + "Kerb_South", "Kerb_North", "Crossing_Markings"): mesh = unreal.EditorAssetLibrary.load_asset(f"/Game/Hollywood/Site/{name}") if mesh is None: continue @@ -103,6 +105,12 @@ def build(): unreal.Rotator(pitch=0.0, yaw=0.0, roll=0.0)) log("staged: Demo_PlayerStart on the north sidewalk facing south") + # A lockable LFS .umap checks out read-only, which makes save_map silently + # return False — chmod u+w first or the staged actors never persist. + umap = os.path.join(unreal.Paths.project_dir(), MAP.replace("/Game/", "Content/") + ".umap") + if os.path.exists(umap) and not os.access(umap, os.W_OK): + os.chmod(umap, 0o644) + log(f"map writable: chmod u+w {umap}") saved = unreal.EditorLoadingAndSavingUtils.save_map( unreal.EditorLevelLibrary.get_editor_world(), MAP) log(f"save_map {MAP}: {saved}") diff --git a/Tools/ue_build_review_map.py b/Tools/ue_build_review_map.py index 168fc5b..950175f 100644 --- a/Tools/ue_build_review_map.py +++ b/Tools/ue_build_review_map.py @@ -29,7 +29,7 @@ def ensure_writable(path): # A packed .umap left read-only (git sets 644->444 on checkout, or the # editor re-flags it) makes save_map silently return False with no error. # chmod u+w it so the staged camera list and actors actually persist. - p = os.path.join(unreal.Paths.project_dir(), path.replace("/Game/", "Content/")) + p = os.path.join(unreal.Paths.project_dir(), path.replace("/Game/", "Content/") + ".umap") try: mode = os.stat(p).st_mode & 0o777 if mode & 0o200 == 0: @@ -229,7 +229,7 @@ try: # 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(-13600, -1650, 165), unreal.Vector(-12722.5, -1450, 500)), + ("demo-crossing", unreal.Vector(-23750, -1800, 480), unreal.Vector(-24291, -1400, 300)), # 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)), diff --git a/Tools/ue_import_site.py b/Tools/ue_import_site.py index ceb0616..bf50a86 100644 --- a/Tools/ue_import_site.py +++ b/Tools/ue_import_site.py @@ -27,7 +27,10 @@ def log(msg): unreal.log(str(msg)) -def import_fbx(path, name, collision): +def import_fbx(path, name, collision="complex"): + # 5.8 note: there is no FBX import 'no collision' switch exposed to Python; + # a default import simply generates no collision primitives, so collision="none" + # means: skip the trace-flag step below and leave the body setup empty. task = unreal.AssetImportTask() task.filename = path task.destination_path = DEST @@ -58,10 +61,16 @@ def import_fbx(path, name, collision): # something to stand on. Terrain and street get complex-as-simple: a simple # hull would ramp straight over the kerbs. # - # UE 5.8 exposes this only through StaticMeshEditorSubsystem (the mesh itself + # UE 5.8 exposes this only through the body setup's trace flag (the mesh itself # has no trace-flag property, and the subsystem has no setter in Python), so - # the import path sets the body setup's trace flag through the mesh's - # collision options at import, then verifies it by reading it back. + # the import path sets the body setup's trace flag, then verifies it by reading + # it back. + if collision == "none": + box = mesh.get_bounding_box() + mn, mx = box.min, box.max + log(f"{name:18} import ok size=({mx.x - mn.x:.0f}, {mx.y - mn.y:.0f}, {mx.z - mn.z:.0f}) cm " + f"min_z={mn.z:.1f} collision=none") + return trace = (unreal.CollisionTraceFlag.CTF_USE_COMPLEX_AS_SIMPLE if collision == "complex" else unreal.CollisionTraceFlag.CTF_USE_SIMPLE_AND_COMPLEX) try: @@ -83,6 +92,11 @@ def main(): return for path in files: stem = os.path.splitext(os.path.basename(path))[0] + # Crossing markings are paint on the road surface — no collision at all, + # so the player never stands on a 1 cm lip at the band edges. + if stem == "Crossing_Markings": + import_fbx(path, stem, "none") + continue collision = "complex" if stem in ("Terrain_DEM", "Road_Hollywood", "Sidewalk_South", "Sidewalk_North") else "simple" import_fbx(path, stem, collision) From 54b62395b8492dbb4998e675da62f8de556e9ae3 Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Sat, 7 Nov 2026 15:47:40 +0100 Subject: [PATCH 5/8] docs: crossing done pending review; record LFS-lockable and normals traps --- MEMORY.md | 16 ++++++++++++++++ Screenshots/README.md | 2 +- TODO.md | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/MEMORY.md b/MEMORY.md index 1989645..a5d9a42 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -147,3 +147,19 @@ Scope: facts and traps that cost time to establish. Planned work lives in [TODO. - **A per-pixel `struct.pack` loop over `img.pixels` is catastrophic in headless Blender** (256² took 25 s). Blender ships numpy: read `img.pixels[:]` once into a float array, reshape `(h,w,4)`, clip→uint8, reverse rows for PNG top-down, then zlib-compress. Whole 1024² encode ≈ 0.3 s. - **MPFB's v2-skin node tree does not resolve to a simple base colour**, so a Cycles DIFFUSE bake returns black. Since the FBX export material replaces the node tree anyway, synthesize the diffuse directly (`make_diffuse`): warm skin body + a darker shorts-suggestive bottom band. - **A read-only `.umap` silently makes `save_map` return `False` with no error** (already recorded); re-hit in the review pipeline — `chmod u+w` the maps before rebuilding, otherwise the new character material never appears in shots. + +## 2026-11-07 — Blender skill packs: discovery depth + +- **The 94 packs were never registered with the `skill` tool**, only readable as files. The loader resolves `//SKILL.md` one level down; `.agents/skills/blender-skills/` is a *container* of 94 packs, so it was skipped silently (no warning, no error). Symptom: `skill("archviz")` unresolvable even though `archviz/SKILL.md` exists. +- **Fix applied:** 94 symlinks in `~/.agents/skills/` → `/.agents/skills/blender-skills//`. The catalog then lists all of them (verified by calling `skill` on `collision-proxy`: base dir resolves through the link). `references/` stays unlinked — it is a shared folder with no `SKILL.md`, not a pack. +- **The AGENTS.md "MCP-driven" caveat is overstated.** 41 of 94 SKILL.md mention MCP, mostly only in the `description` line; the bodies are method guides (naming, proportions, validation checklists) that work headless. Do not skip a pack because it advertises MCP. +- **Skill names in AGENTS.md/TODO.md all resolve** except `references`, which is the folder above, not a pack. +- Symlinks are user-level: a fresh machine needs the same 94 links before the packs are loadable by name. + + +## 2026-11-07 — Street visibility and the LFS lockable trap + +- **Every street surface (road, sidewalks, kerbs, markings) had 100 % downward face normals** — `strip()`'s natural winding (rows along +s, cols along +normal) faces the ground. Invisible in engine for as long as the terrain covered them; the corridor cut exposed the black trench. Fixed by reversing the winding in `strip()`, `kerb_faces()` and `crossing_band()`. +- **`*.blend` and `*.umap` are LFS-lockable in `.gitattributes`** — Git LFS checks them out read-only (444). `bpy.ops.wm.save_as_mainfile` then fails ("path is not writable") and `EditorLoadingAndSavingUtils.save_map` silently returns False. All Blender builders now chmod u+w before saving and treat a failed save as fatal; the UE map builders chmod the .umap first. A failed save used to leave downstream steps building from stale files — that produced a degenerate-mesh FBX (all-same vertex indices from an index-arithmetic bug) that cost an hour to trace. +- **FBX SDK refuses to triangulate the crown-twisted crossing quads** ("unable to triangulate mesh ... will be omitted") — emit triangles from the builder, and make sure per-stripe vertex indices advance (`len(verts)` must be captured before the loop, not per-corner). +- Demo-map coordinate note: UE Y is south-positive; the facade at Y −1450 and the player start at Y −3500 are on opposite sides of the road (Y −1382..+1047) — arrangement is correct; the Sidewalk_North/South mesh names are swapped relative to their physical sides (documented in export_site_fbx.py). diff --git a/Screenshots/README.md b/Screenshots/README.md index fecc2d2..82d5528 100644 --- a/Screenshots/README.md +++ b/Screenshots/README.md @@ -54,7 +54,7 @@ Rendered from `/Game/Hollywood/DemoMap`, the walkable level. Click an image for | --- | --- | --- | --- | | `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) | needs review | From the west, looking along the block (foreground blocking) | +| `demo/review-demo-crossing.png` | [crossing](demo/review-demo-crossing.png) | needs review | 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 | ## What to look for diff --git a/TODO.md b/TODO.md index ef1f9e2..6d25537 100644 --- a/TODO.md +++ b/TODO.md @@ -94,7 +94,7 @@ The MPFB character asset is planned as the NPC template (see `ROADMAP.md`). Keep - [x] First-person movement, mouse look, and Enhanced Input bindings. Implemented 2026-11-07: `AGigLineCharacter` (C++, 1.75 m capsule, 1.60 m eye camera, 4 m/s walk, mouse look in deg/s, jump) as the default pawn, with five actions and an 11-entry context in `/Game/Input` built by `Tools/ue_create_input_assets.py`. **Not yet confirmed interactively** — launch PIE on `Test/ReviewMap` and walk. Screenshot review pending (`Screenshots/README.md`). - [~] 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. - [-] 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/` are ☐ pending review. -- [ ] 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`). +- [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 review pending (`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. - [ ] Geographic attribution display ("© OpenStreetMap contributors" + license link). From ab24ef3b24043ec78b151f670319af7d19efcf2a Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Sat, 7 Nov 2026 15:48:17 +0100 Subject: [PATCH 6/8] memory: record the shared-checkout commit race The skill-pack entry was swept into another session's commit before it could be committed on its own. Notes that a shared worktree can commit a shared file for you - or drop the edit if the file is overwritten - and that git log -S is the way to check before assuming the edit was lost. --- MEMORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MEMORY.md b/MEMORY.md index a5d9a42..525addd 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -155,6 +155,7 @@ Scope: facts and traps that cost time to establish. Planned work lives in [TODO. - **The AGENTS.md "MCP-driven" caveat is overstated.** 41 of 94 SKILL.md mention MCP, mostly only in the `description` line; the bodies are method guides (naming, proportions, validation checklists) that work headless. Do not skip a pack because it advertises MCP. - **Skill names in AGENTS.md/TODO.md all resolve** except `references`, which is the folder above, not a pack. - Symlinks are user-level: a fresh machine needs the same 94 links before the packs are loadable by name. +- **A shared checkout can commit your uncommitted edit for you, or lose it.** This entry was written but not yet committed when another session ran a `git commit` that included `MEMORY.md`; it landed inside their commit (`54b6239`) with a message that does not mention it. Had they overwritten the file instead, the work would have been gone with no reflog to recover from. On a shared worktree, either commit a shared file immediately or check `git log -S` for it before assuming the edit is missing. ## 2026-11-07 — Street visibility and the LFS lockable trap From c9e933376c259363acfd314684eb8786b1986d09 Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Sat, 7 Nov 2026 16:23:59 +0100 Subject: [PATCH 7/8] 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. --- AGENTS.md | 2 +- Game/GigLine/Content/Hollywood/DemoMap.umap | 4 +- Game/GigLine/Content/Test/ReviewMap.umap | 4 +- MEMORY.md | 6 ++ Screenshots/README.md | 9 +-- 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 | 6 +- Tools/ue_build_demo_map.py | 14 ++++- Tools/ue_build_review_map.py | 60 ++++++++++--------- 22 files changed, 92 insertions(+), 69 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 473577b..3aa4450 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -101,7 +101,7 @@ Any change that affects the game's appearance — meshes, materials, lighting, l 1. Run `Tools/render_screenshots.sh` (rebuilds `Game/GigLine/Content/Test/ReviewMap.umap` from current content, runs the game on it, captures the fixed camera list, copies the PNGs into `Screenshots/`). 2. Update `Screenshots/README.md`: one row per screenshot with what it shows and an approval state; describe any new shots or scene changes in the same change. 3. The PNGs **are committed** so reviewers see exactly what the build rendered (`.gitignore` does not exclude them). Keep them in sync with this README: drop retired shots in the same change. -4. Wait for the user's approval before treating a visual change as done; record approval by ticking the table in `Screenshots/README.md`. +4. Wait for the user's approval before treating a visual change as done; record approval by ticking the entry in `Screenshots/README.md`. New content that needs a different camera angle gets a new entry in the shot list in `Tools/ue_build_review_map.py`. diff --git a/Game/GigLine/Content/Hollywood/DemoMap.umap b/Game/GigLine/Content/Hollywood/DemoMap.umap index 5230027..97dc8de 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:68526b7095cfca222b500ff6968eb687ad1771cb31d0a58cbb7dace518968210 -size 32372 +oid sha256:2a2b7a61aa8b3c86c3944849107aa00d3ab6fa0d35056d50b02a65acbd4854a1 +size 33816 diff --git a/Game/GigLine/Content/Test/ReviewMap.umap b/Game/GigLine/Content/Test/ReviewMap.umap index 816d9a2..8db2af7 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:208a1670a337ad19c75ee4b58bfd997dc6018f770ea9edaf85680f6bf723f10c -size 32736 +oid sha256:4d399b3e1b28616e58f445fca8a7de82e9bf1df7c4308500473fc0c63d6d597d +size 28613 diff --git a/MEMORY.md b/MEMORY.md index 525addd..76159ab 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -164,3 +164,9 @@ Scope: facts and traps that cost time to establish. Planned work lives in [TODO. - **`*.blend` and `*.umap` are LFS-lockable in `.gitattributes`** — Git LFS checks them out read-only (444). `bpy.ops.wm.save_as_mainfile` then fails ("path is not writable") and `EditorLoadingAndSavingUtils.save_map` silently returns False. All Blender builders now chmod u+w before saving and treat a failed save as fatal; the UE map builders chmod the .umap first. A failed save used to leave downstream steps building from stale files — that produced a degenerate-mesh FBX (all-same vertex indices from an index-arithmetic bug) that cost an hour to trace. - **FBX SDK refuses to triangulate the crown-twisted crossing quads** ("unable to triangulate mesh ... will be omitted") — emit triangles from the builder, and make sure per-stripe vertex indices advance (`len(verts)` must be captured before the loop, not per-corner). - Demo-map coordinate note: UE Y is south-positive; the facade at Y −1450 and the player start at Y −3500 are on opposite sides of the road (Y −1382..+1047) — arrangement is correct; the Sidewalk_North/South mesh names are swapped relative to their physical sides (documented in export_site_fbx.py). + +## 2026-11-07 — ReviewMap corruption: deprecated level calls + save-order bug + +- **`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). diff --git a/Screenshots/README.md b/Screenshots/README.md index 82d5528..a022080 100644 --- a/Screenshots/README.md +++ b/Screenshots/README.md @@ -20,14 +20,15 @@ and exits — about a minute. The camera list lives in ## Scene conventions - **Floor**: 160 m square, top face at Z 0. The character measures Z 0..83 cm. -- **Cubes**: `Cube_ScaleTest_A` floats at Z 2 m, `Cube_ScaleTest_B` rests on the - floor; both are exactly 1 m — the scale reference. +- **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. ## Review-map shots -All ☑ approved 2026-11-07 unless noted. `review-npc-prefab` is new (☐ pending). +All ☑ approved 2026-11-07 (re-rendered with the character and cubes restored; +user visual check passed). > Naming: shots use their stable `Tools/ue_build_review_map.py` camera name (no > index prefix), so adding or reordering cameras never renumbers files or @@ -54,7 +55,7 @@ Rendered from `/Game/Hollywood/DemoMap`, the walkable level. Click an image for | --- | --- | --- | --- | | `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) | needs review | 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-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 | ## What to look for diff --git a/Screenshots/demo/review-demo-crossing.png b/Screenshots/demo/review-demo-crossing.png index 408995a..041f7ed 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:e7791340703cf239fa953504f2b38ce6a96ec131e35e622078acb51d0957e763 -size 1292359 +oid sha256:b6bd66678c132a8fa09004f2f2e3d160b1c99122e1d1365932d7e31ed0933199 +size 1628670 diff --git a/Screenshots/demo/review-demo-north-buildings.png b/Screenshots/demo/review-demo-north-buildings.png index b39fbc2..137a99a 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:95f580993db879516ee1f4f243b6cd00abad523d10cf23b1cb1a39324365de05 -size 1775921 +oid sha256:5eaa60227e5f6e307e1bf878b33d3f52634e0121dcaa6af73afd7fff5728499b +size 2304425 diff --git a/Screenshots/demo/review-demo-spawn-view.png b/Screenshots/demo/review-demo-spawn-view.png index c965b34..9132d32 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:788f71de1bf5080c738da6d94170e3d5a577823cd33aa5a10dd892f39b41518f -size 2585447 +oid sha256:a312b5ee86cd5c9fef8b686d2aae429199f88caae7276d6fb714d1d73fed996a +size 2571043 diff --git a/Screenshots/demo/review-demo-street-level.png b/Screenshots/demo/review-demo-street-level.png index 67951c1..86a9834 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:292f503ebbd786d87c44ad2a98ef5458bef582134c59c34ae5cfd24331268ba6 -size 1932918 +oid sha256:2675eb353573003961c8d81ba24c2bbbe1f1052c11c4ee38cd786cab7115b686 +size 1975605 diff --git a/Screenshots/review-character.png b/Screenshots/review-character.png index dfb05f8..b550695 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:4f7e7731e6228ba7954947545a02066e415989a8bf2aab96c4113891a78221d7 -size 2202483 +oid sha256:ea60ed98f688bab482160386d851b39a10868c64bf1371b3628427cc25a3de97 +size 2498425 diff --git a/Screenshots/review-ground-contact.png b/Screenshots/review-ground-contact.png index 516451f..2c74a02 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:3c20c78e6c9a109dea7e0aafd0ec9cfe799ca5cf5961398c3a369481ac675af2 -size 1705468 +oid sha256:03b47847338f6d840e1e2dd370fce585c599cd72db0cf1393b82cc91964619c0 +size 1778455 diff --git a/Screenshots/review-hero-facade-front.png b/Screenshots/review-hero-facade-front.png index e267105..564b65c 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:3369c4eccf8902a9e84056b0fe419735b29983d08ae26ba112374cf601c15df0 -size 2304244 +oid sha256:fabcc154b139f35e0c6910f87a042c6cc51b9e263993cbfb49a15fa3817736d3 +size 2052242 diff --git a/Screenshots/review-hero-facade-oblique.png b/Screenshots/review-hero-facade-oblique.png index 3a7cee3..07d778c 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:a77f2f03eee7a351713937387cdb2dafe590e607f3960c18c31d600d2f9c7d47 -size 2404004 +oid sha256:20a85e17ab952b5f8de52556bbe9bf7b4dfb03c7e430a371eafa625b0874b4d7 +size 1917582 diff --git a/Screenshots/review-hero-portals.png b/Screenshots/review-hero-portals.png index 5db95d1..69194a1 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:f5c65c6f974a5ef490a0da4a36fd8d237211d3efa0540040eb0ef3bca4a7d137 -size 1794383 +oid sha256:8fa065612a714476d37f5cc4bb28182b30dc9b10cfcfd37cc04257d177b41f33 +size 1774919 diff --git a/Screenshots/review-npc-prefab.png b/Screenshots/review-npc-prefab.png index 42230dd..b10ff0f 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:1a52cf51af9cfb70e788c0e6d81cb33737380bc8c3adb4f72a9c8c2ef9aa74f5 -size 1660515 +oid sha256:c1522a746881f69e8fb2e327610ad9fd7d98e673c200ea8d781daab1f23317cc +size 1721860 diff --git a/Screenshots/review-overview.png b/Screenshots/review-overview.png index 9a2acc1..d518bc4 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:068f9cd7209a02f9e6e1f6c6babfefa43507992600eb7086fbea9792367bb379 -size 2127805 +oid sha256:78b318299a7633fb267c961ecfd61f5fe7a0a3278d28103b634d21f47bc01c49 +size 2580045 diff --git a/Screenshots/review-player-placement.png b/Screenshots/review-player-placement.png index a256838..f39f1df 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:9b766e74af88547fe201766bc118bbea70b478e9f7ff9f9ac4251343bd39fb8e -size 1847345 +oid sha256:605fc1ee8ef167a57d119ef83e741ab1e6d8fbba923bfe5aa162f0dda1069830 +size 1969709 diff --git a/Screenshots/review-scale-cubes.png b/Screenshots/review-scale-cubes.png index f6cf6ae..8fc47eb 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:a0c2a93e30216c76e7db5ad28ce9b0e580cfcd8c269b1494cdc65b6ce57b788a -size 2066648 +oid sha256:26c64adad9f64789c85882a224d402ae4e8ca20eb73736731925972275fc560d +size 1906660 diff --git a/Screenshots/review-shadow-compare.png b/Screenshots/review-shadow-compare.png index cb38caa..2d975fa 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:e708d4d8b765ec270b57af26a1134ccd7fcc34d55123634599a316d8dc9d4327 -size 2052859 +oid sha256:bab0f21cc8a6c90889e0003510b7df68f6f23d16e69e9ba08faec40deef77c8b +size 1735259 diff --git a/TODO.md b/TODO.md index 6d25537..9b78b7c 100644 --- a/TODO.md +++ b/TODO.md @@ -75,7 +75,7 @@ The MPFB character asset is planned as the NPC template (see `ROADMAP.md`). Keep - [x] Give the MPFB character asset a standing idle pose so review and NPC shots show a person, not an A-pose bind. Done: `Tools/mpfb_character_export.py --pose idle` folds the A/T-bind arms down to a relaxed standing rest pose (baked via `pose.armature_apply` before export; `upperarm01.L` rest z −0.71 → −1.91 rad), re-exported, re-imported (138 bones), rendered in `review-01-character`. - [x] Bake a second skin tone or clothing layer, so the character does not read as a grey mannequin in shots. Done: the v2-skin bake came out **black** and slow at 1024², so we synthesize a warm skin-tone diffuse directly (`Tools/mpfb_character_export.py` `make_diffuse`, with a darker shorts-suggestive bottom band) via a stdlib/numpy PNG writer. Character reads warm, not grey. Screenshot approved (`Screenshots/review-01-character`). -- [x] Keep the character as a reusable NPC prefab: a visible body, a collision capsule, and a head-height check that the camera does not clip at 1.60 m — for when NPCs/pedestrians are wired in (`0.2.0+`), not the `0.1.0` player. Done 2026-11-07: `AGigLineNPCPerson` (C++), ACharacter with capsule half-height 83 cm / total 1.66 m (head at ~1.66 m, so a 1.60 m camera does not clip), the MPFB human on the built-in mesh as the visible body, `AutoPossessAI` for pedestrians; confirmed spawned feet-on-Z-0 and rendered in `Screenshots/review-npc-prefab` (☐ pending approval). +- [x] Keep the character as a reusable NPC prefab: a visible body, a collision capsule, and a head-height check that the camera does not clip at 1.60 m — for when NPCs/pedestrians are wired in (`0.2.0+`), not the `0.1.0` player. Done 2026-11-07: `AGigLineNPCPerson` (C++), ACharacter with capsule half-height 83 cm / total 1.66 m (head at ~1.66 m, so a 1.60 m camera does not clip), the MPFB human on the built-in mesh as the visible body, `AutoPossessAI` for pedestrians; confirmed spawned feet-on-Z-0 and rendered in `Screenshots/review-npc-prefab` (☑ approved 2026-11-07). - [x] Screenshots use stable camera names (no index prefix), so inserting a shot no longer renumbers/ orphans files; `AGigLineShotActor` + `Screenshots/README.md` updated. ### Asset production @@ -93,8 +93,8 @@ The MPFB character asset is planned as the NPC template (see `ROADMAP.md`). Keep - [x] First-person movement, mouse look, and Enhanced Input bindings. Implemented 2026-11-07: `AGigLineCharacter` (C++, 1.75 m capsule, 1.60 m eye camera, 4 m/s walk, mouse look in deg/s, jump) as the default pawn, with five actions and an 11-entry context in `/Game/Input` built by `Tools/ue_create_input_assets.py`. **Not yet confirmed interactively** — launch PIE on `Test/ReviewMap` and walk. Screenshot review pending (`Screenshots/README.md`). - [~] 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. -- [-] 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/` are ☐ pending review. -- [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 review pending (`Screenshots/demo/review-demo-crossing`); the corridor renders near-black in shadow — that is the still-open **Fixed daytime lighting** item, not the crossing. +- [-] 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. - [ ] Geographic attribution display ("© OpenStreetMap contributors" + license link). diff --git a/Tools/ue_build_demo_map.py b/Tools/ue_build_demo_map.py index 8e6a6a7..a79107d 100644 --- a/Tools/ue_build_demo_map.py +++ b/Tools/ue_build_demo_map.py @@ -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 diff --git a/Tools/ue_build_review_map.py b/Tools/ue_build_review_map.py index 950175f..bd48a94 100644 --- a/Tools/ue_build_review_map.py +++ b/Tools/ue_build_review_map.py @@ -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") From fbb3c3fb99465a2444ddcd53f1b397c03f0a8abd Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Sat, 7 Nov 2026 16:24:19 +0100 Subject: [PATCH 8/8] =?UTF-8?q?agents:=20seed=20AGENTS.md=20from=20PLAN.md?= =?UTF-8?q?=20=E2=80=94=20agent=20instructions=20source?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 229 +++++++++++++++++++++++------------------------------- 1 file changed, 96 insertions(+), 133 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 3aa4450..436825f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,32 +1,53 @@ -# AGENTS.md +# PLAN.md -Working rules for autonomous agents, AI coding assistants, and contributors. Planned work is tracked in **[TODO.md](TODO.md)**; development direction is in **[ROADMAP.md](ROADMAP.md)**. +## Goal -## Project Goal +Build a Linux-first Unreal Engine game for walking and driving through a real city. -Develop and test on Linux; ship Windows and Linux builds, with Linux as the primary development and CI platform. The game supports walking and driving through a real city. -The initial release is **`0.1.0`**: a walkable exterior demo around **6840 Hollywood Blvd / El Capitan Entertainment Centre**, including buildings on the south side of Hollywood Blvd between Highland Avenue and Orange Drive, plus buildings whose footprint lies within 50 m of the north-side frontage. +**Initial release: `0.1.0`** — a walkable exterior demo around **6840 Hollywood Blvd / El Capitan Entertainment Centre**, including the surrounding block frontage and buildings directly across Hollywood Boulevard. -- Verify the address, building footprints, and demo boundaries **before** modeling. -- If the address, footprint, or boundary cannot be verified from permitted sources, do not model that structure. Record the unresolved item in `Geo/` with the sources checked and continue with verified structures only. -- Treat similarly named venues as **distinct structures** where source data indicates; do not merge footprints by assumption. -- use available research and reference data in local project `/run/media/system/DATA/Documents/06-games/security-game/`. +Verify the address, building footprints, and demo boundaries before modeling. Treat these as distinct structures where the source data indicates; do not assume similarly named venues share one footprint. -## Toolchain +## Stack -| Tool | Role | -| --- | --- | -| Unreal Engine | Rendering, gameplay, World Partition, Chaos Vehicles | -| Blender | Modeling, UVs, baking, collision meshes, character prep | -| MPFB | Human base meshes; baked materials, deform skeletons | -| QGIS | Geographic cleanup, coordinate conversion, elevation prep | -| Blosm | OSM building/street blockouts in Blender | -| Git + Git LFS | Source control, large assets, tagged releases | +- **Unreal Engine:** rendering, gameplay, World Partition, Chaos Vehicles. +- **Blender:** modeling, UVs, baking, collision meshes, character preparation. +- **MPFB:** human base meshes; export baked materials and deform skeletons. +- **QGIS:** geographic cleanup, coordinate conversion, elevation preparation. +- **Blosm:** OSM building and street blockouts in Blender. +- **Git + Git LFS:** source control, large assets, tagged releases. -- Pin compatible versions in `Docs/toolchain.md`. -- Validate Blender→Unreal mesh, skeleton, material, and scale transfers **before** producing assets. +Pin compatible tool versions in `docs/toolchain.md`. Validate Blender-to-Unreal mesh, skeleton, material, and scale transfers before producing assets. -## Repository Layout +## Release Policy + +Use Semantic Versioning: `MAJOR.MINOR.PATCH`. + +- Each major development milestone receives a **minor release**. +- Each completed small stabilization task receives a **patch release**. +- New milestone functionality lands in prereleases such as `0.2.0-alpha.1`, not in patches. +- Patches contain fixes, optimization, documentation, or refinements without expanding scope. +- Tag releases as `v0.1.0`; maintain `CHANGELOG.md`. +- During `0.x`, document breaking changes and asset migrations. +- `1.0.0` marks the stable, agreed game scope—not completion of an entire city. + +## Initial Local Setup + +Target workstation: Bazzite Linux, Ryzen 7 7700X, 64 GB RAM, RTX 5070 Ti 16 GB. + +1. Update Bazzite using its supported mechanism; verify NVIDIA/Vulkan operation. +2. Register an Epic account and accept the Unreal license. Obtain a pinned Linux engine build or build from Epic's authorized source repository. +3. Validate Unreal on Bazzite before asset production; it is not necessarily an officially supported distribution. +4. Install the engine-required compiler/SDK, Git, Git LFS, and an editor. Use Distrobox for isolated command-line dependencies where useful. +5. Pin Blender, MPFB, Blosm, and QGIS versions; confirm add-on compatibility. +6. Keep the project and Derived Data Cache on SSD storage. Reserve several hundred GB for engine files, assets, caches, and builds. +7. Create a C++ Unreal project with Blueprint gameplay iteration and Enhanced Input. +8. Package and launch a blank Linux build immediately. +9. Test one static mesh and one MPFB character export before proceeding. + +### Repository + +Progress lives in `ROADMAP.md`; session findings live in `MEMORY.md`; rendered review captures and their approval state live in `Screenshots/`. - `Game/` — Unreal project, configuration, source, game-ready content. - `Art/` — editable Blender files and original textures. @@ -34,132 +55,74 @@ The initial release is **`0.1.0`**: a walkable exterior demo around **6840 Holly - `Tools/` — repeatable import/export helpers. - `Docs/` — setup, licenses, architecture, release checks. -**Git/LFS rules:** +Track `.uasset`, `.umap`, `.blend`, and large source binaries with LFS. +Ignore generated `Binaries/`, `Intermediate/`, `Saved/`, and Derived Data Cache. +Do not commit Unreal Engine source, credentials, or non-redistributable assets. -- Track `.uasset`, `.umap`, `.blend`, and large source binaries with LFS. -- Ignore `Binaries/`, `Intermediate/`, `Saved/`, and Derived Data Cache. -- Never commit Unreal Engine source, credentials, or non-redistributable assets. -- Active remote repository (`origin`): `https://git.allucanget.biz/zwitschi/gig-line.git`. Verify connectivity before use. +## Geographic and Art Pipeline -## Release Policy - -Semantic Versioning: `MAJOR.MINOR.PATCH`. - -- Each major milestone → **minor** release (e.g. `0.2.0`, possibly as `0.2.0-alpha.1` first). -- Each release containing only bug fixes, performance changes, documentation, or tuning of existing features → **patch** release (e.g. `0.2.1`). Any new feature, map area, or asset category → **minor** release. -- Patches contain fixes, optimization, docs, or refinements **only** — never new scope. -- Tag as `vX.Y.Z`; maintain `CHANGELOG.md`. -- During `0.x`, document breaking changes and asset migrations. -- `1.0.0` marks the stable, agreed game scope — not completion of a whole city. - -## Geographic & Art Pipeline - -1. Save a versioned OSM extract; document date, bounds, and source. -2. Acquire separately licensed elevation data for the demo bounds unless the source DEM shows less than 1 m height variation across the demo area; record the decision in `Geo/` metadata. -3. In QGIS, use a suitable local projected CRS (e.g. EPSG:26911) and record a local origin. +1. Save a versioned OSM extract and document its date, bounds, and source. +2. Acquire separately licensed elevation data if needed. +3. In QGIS, use a suitable local projected CRS, such as EPSG:26911, and record a local origin. 4. Import OSM blockouts through Blosm; clean and detail them in Blender. -5. Preserve geographic coordinates in metadata; convert to local Unreal coordinates with explicit meter→centimeter conversion. +5. Preserve geographic coordinates in metadata; convert to local Unreal coordinates with explicit meter-to-centimeter conversion. 6. Export modular buildings, sidewalks, roads, collision, and materials. -7. Use reference photography for manual façade work, ignore licensing for internal development use and do not commit reference images to the repository. -8. Treat OSM as **layout data only** — not authoritative façade, height, or pavement detail. +7. Use licensed reference photography for manual façade work. +8. Treat OSM as layout data—not authoritative façade, height, or pavement detail. -Always use a consistent scale test, named export presets, and repeatable import settings. +Use a consistent scale test, named export presets, and repeatable import settings. -## Blender skills +## Initial Demo: `0.1.0` -`.agents/skills/blender-skills/` holds 94 Blender skill packs (MIT, blender-skills 1.3.0). They are agent instructions, not add-ons, and nothing in the pipeline depends on them. +### Included -**All agents must consult the relevant skill packs before starting a task in that discipline.** Each patch section in `TODO.md` lists its suggested skills; the general mapping is: +- Named site frontage, surrounding block frontage, and buildings across the street. +- Walkable sidewalks and a designated street crossing. +- First-person movement, mouse look, collision, reset, and pause. +- Recognizable hero façades; simplified background buildings. +- Fixed daytime lighting, basic ambience, and geographic attribution. +- Packaged Linux build with documented controls. -- **Modeling / procedural assets** — `blender-modeler`, `procedural-modeling`, `geometry-nodes`, `hard-surface`. -- **Façades and architecture** — `archviz`, `historical-worlds` (1920s Hollywood Blvd direction), `realistic-style`. -- **Materials and textures** — `materials`, `texture-workflow`, `lookdev`. -- **UVs and export** — `uv-workflow`, `export-pipeline`, `unreal-export` (verify against the `ue_metric_v1` / `ue_character_v1` presets in `Tools/`). -- **Collision** — `collision-proxy` (UCX/UHX naming, hulls, simple colliders). -- **Scene assembly / optimization** — `scene-assembly`, `asset-optimization`, `lod-pipeline`. -- **Review and QA** — `qa-review`, `references` (reference-image-match and visual-match checklists), `camera-cinematography` (matching reference camera angles). +### Excluded -**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. +Driving, interiors, crowds, traffic simulation, multiplayer, and a full-city map. -## Licensing & Distribution +### Acceptance + +- Player can traverse both sides of the demo without falling through geometry. +- Building placement, street scale, and site identity have been checked against references. +- No blocking collision or packaging defects. +- Target 60 FPS at 1440p on the development workstation using a documented quality preset. +- Record whether upscaling is enabled and report performance without frame generation. +- A fresh project checkout builds with the documented external engine installation. + +## Indicative Timeline + +Solo-development estimate: **approximately 30–40 weeks**, subject to asset quality and available hours. + +| Release | Weeks | Larger task | Small patch tasks after release | +| --- | ---: | --- | --- | +| `0.1.0` | 1–6 | Local setup and initial walkable Hollywood block | `0.1.1` collision fixes; `0.1.2` scale/façade corrections; `0.1.3` packaging and setup fixes | +| `0.2.0` | 7–9 | MPFB player body, third-person camera, basic animation | `0.2.1` rig fixes; `0.2.2` skin/hair tuning; `0.2.3` camera clipping fixes | +| `0.3.0` | 10–13 | One Chaos vehicle, enter/exit interaction, closed driving route | `0.3.1` handling tuning; `0.3.2` vehicle collision fixes; `0.3.3` input fixes | +| `0.4.0` | 14–17 | Expand to a small connected neighborhood; stream map cells | `0.4.1` seam fixes; `0.4.2` streaming corrections; `0.4.3` HLOD tuning | +| `0.5.0` | 18–21 | Pedestrians, crosswalk behavior, limited ambient traffic | `0.5.1` navigation fixes; `0.5.2` crossing fixes; `0.5.3` simulation optimization | +| `0.6.0` | 22–24 | Visual/audio pass and evening lighting preset | `0.6.1` lighting fixes; `0.6.2` material corrections; `0.6.3` audio balancing | +| `0.7.0` | 25–27 | Settings, control rebinding, save/load, accessibility basics | `0.7.1` save fixes; `0.7.2` UI fixes; `0.7.3` input persistence fixes | +| `0.8.0` | 28–30 | Performance budgets and broader Linux hardware testing | `0.8.1` VRAM reductions; `0.8.2` frame-time fixes; `0.8.3` compatibility fixes | +| `0.9.0` | 31–34 | Feature freeze, public beta, license and build audit | `0.9.1` blocker fixes; `0.9.2` regression fixes; `0.9.3` documentation fixes | +| `1.0.0` | 35–40 | Stable neighborhood-scale walking/driving release | `1.0.x` maintenance and compatibility fixes | + +Finish each milestone's release checks before expanding scope. Cut map size and simulation complexity before compromising the core walking/driving experience. + +## Licensing and Distribution - Publish project-owned code under a chosen open-source license. -- Unreal remains proprietary/source-available; this is **not** a fully open-source stack. -- Keep code, art, engine, and geographic-data licensing **separate**. +- Unreal remains proprietary/source-available; this is **not a fully open-source engine stack**. +- Separate code, art, engine, and geographic-data licensing. - Record asset provenance and redistribution permissions in `THIRD_PARTY_NOTICES.md`. - Verify licenses for every MPFB asset, texture, animation, vehicle, and audio file. -- An asset whose license cannot be verified must not be committed. Record it as unresolved in `THIRD_PARTY_NOTICES.md` and replace it with a redistributable asset before the release. -- Include "© OpenStreetMap contributors" and the appropriate license link; review ODbL obligations for distributed derived databases. -- Do **not** use extracted Google imagery or geometry without explicit applicable rights. +- Include “© OpenStreetMap contributors” and the appropriate license link; review ODbL obligations for distributed derived databases. +- Do not use extracted Google imagery or geometry without explicit applicable rights. - Keep restricted assets out of the public repository; prefer redistributable replacements. - Review Epic distribution requirements before publishing builds. - -## Visual review (screenshots) - -Any change that affects the game's appearance — meshes, materials, lighting, levels, character, camera, post-processing — must be rendered for human review: - -1. Run `Tools/render_screenshots.sh` (rebuilds `Game/GigLine/Content/Test/ReviewMap.umap` from current content, runs the game on it, captures the fixed camera list, copies the PNGs into `Screenshots/`). -2. Update `Screenshots/README.md`: one row per screenshot with what it shows and an approval state; describe any new shots or scene changes in the same change. -3. The PNGs **are committed** so reviewers see exactly what the build rendered (`.gitignore` does not exclude them). Keep them in sync with this README: drop retired shots in the same change. -4. Wait for the user's approval before treating a visual change as done; record approval by ticking the entry in `Screenshots/README.md`. - -New content that needs a different camera angle gets a new entry in the shot list in `Tools/ue_build_review_map.py`. - -## Roadmap and TODO - -The two files have separate roles: - -- **[TODO.md](TODO.md)** is the single, central task list for all agents: checkbox items, progress marks, and active-section ownership. Put all planned actionable work and follow-ups there. Do not create parallel task lists elsewhere. -- **[ROADMAP.md](ROADMAP.md)** holds the development plan: milestone goals, scope, decisions, and release gates for each version. It is a planning document and contains **no checkbox items**. Read it for future direction; record scope changes there as prose, and record the resulting tasks in `TODO.md`. - -Rules for `TODO.md`: - -- Mark items **open** (`- [ ]`), **in progress/active** (`- [-]` or `- [~]`), or **done** (`- [x]`). Keep the marks accurate. -- When working on a level-three (`###`) section, append `- active - ` to its heading, using your current session ID. Remove the suffix when you stop working on that section. Mark the items you are working on `- [-]` or `- [~]`. -- Existing in-progress marks can describe unfinished work, not necessarily a live session; use the active heading and assignment details to identify current ownership. -- Each version is a `##` heading; task groups are `###` headings with `- [ ]` steps. -- When **all** steps in a version section are done: write a short descriptive entry to `CHANGELOG.md`, remove the version section from `TODO.md`, and leave its goal in `ROADMAP.md`. -- Release checks for each version are listed at the end of its `TODO.md` section; `Docs/release-checks.md` defines the checks themselves. If a check fails, do not tag the release; open a patch task in `TODO.md` and report the failing check in `CHANGELOG.md`. -- Finish each milestone's release checks **before** expanding scope. Cut map size and simulation complexity before compromising the core walking/driving experience. - -## Git workflow - -Every agent works on a dedicated git branch. **Never commit work directly to `main`** (except the coordinator's integration merges). - -Before starting any task: - -1. **Scan `TODO.md`** and pick a task — a level-three (`###`) section — to work on. Use `ROADMAP.md` for the goal and scope of the version you are working in. Prefer sections that are already active (they may be yours) or open items in sections that are not owned by another agent. Check the active heading and assignment details to identify ownership. -2. **Create a branch** for that task from the current `main`: `git checkout -b -` (e.g. `git checkout -b facade-hero-0.1.4`). One branch per task; do not stack unrelated tasks on one branch. -3. **Record the active branch in the heading** of the section you are working on in `TODO.md`: append `- active - ` (and, if useful, the branch name) to the level-three heading. This is the only way other agents can see that work is in progress on this section. -4. **Commit changes often** on that branch — small, focused commits with clear messages. Do not accumulate large uncommitted changes. -5. **Push to remote when a checklist item is done** — each completed `- [x]` item in that section should be committed and pushed. This keeps the remote state visible and makes progress recoverable. -6. When the section is complete, report the branch, changes, checks, and unresolved issues to the coordinator for integration (see *Parallel agent workflow* below). Do not merge into `main` yourself unless authorized. - -**Branch hygiene:** - -- Branch from the current `main` revision, not from a stale local checkout. -- Keep `TODO.md` checkbox updates on your branch — they are part of the work. -- If a task needs a different camera angle, or affects visual review, follow the *Visual review* section before pushing. -- If a task is abandoned, remove the `- active - ` suffix from the heading in `TODO.md` and leave the branch for the coordinator to clean up. - -## Parallel agent workflow - -- Designate one coordinator to serialize assignments, updates to the shared `TODO.md`, and integration into `main`. Agree on task and file or asset ownership before work starts; avoid assigning the same section or binary asset to multiple agents at once. -- The coordinator records the session ID, branch name, and owned files or assets alongside the active `TODO.md` section on the shared `main` coordination copy. Publish and synchronize this state through the remote when agents use different machines. Branch-local changes are not visible to other worktrees until integrated; `TODO.md` is not an atomic lock. -- For parallel implementation, use a separate branch and Git worktree per agent, based on the agreed `main` revision. Do not switch branches in a directory another agent is using. Create branches, commits, pushes, and merges only when authorized; never overwrite another agent's uncommitted work. -- Keep ignored local references, generated files, build outputs, and Unreal editor processes in mind: worktrees do not automatically share ignored files, and shared output paths or concurrent editors can collide. Arrange required inputs and isolated outputs before running tools. -- Blender and Unreal binary assets cannot be meaningfully merged like text. Give each asset one owner; use Git LFS locks when supported by the remote, but do not assume locking is available. -- When work is ready, report the branch, changes, checks, unresolved issues, and relevant memory findings to the coordinator. Review and integrate branches into `main` one at a time, reconcile `TODO.md` and memory changes, and update ownership and status. Follow the visual review and user-approval requirements before marking visual work done. - -## Memory - -**[MEMORY.md](MEMORY.md)** is shared session memory for agents and collaborators. Write to it whenever you: - -- discover a fact about the environment, a tool, or a data source that cost time to establish; -- hit a gotcha, trap, or surprising failure mode, and what actually caused it; -- find or fix a bug, and how it was found so it can be found again; -- make a decision that does not fit a `TODO.md` item, such as a workaround, an assumption, or a rejected approach and why. Record scope or direction decisions in `ROADMAP.md` as prose. - -Keep entries short and dated, with the evidence: the command, the log line, or the file path. Anything that belongs in a formal document (setup steps, tool versions, licensing) goes in `Docs/` as well — memory is for coordination, not a replacement for the record. Do not let `MEMORY.md` become a second task list: planned work stays in `TODO.md`, and development direction stays in `ROADMAP.md`. - -Memory edits on an agent's branch are not shared live. Communicate important findings to the coordinator during work and reconcile them into the shared `main` copy when integrating.