From 71acffec12ce794d78300504b45973c8046daec3 Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Sun, 8 Nov 2026 14:55:58 +0100 Subject: [PATCH] =?UTF-8?q?qa(collision):=20no-blocking-collision=20releas?= =?UTF-8?q?e=20check=20=E2=80=94=20seam=20+=20wall=20audit=20(#13)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merged PR #13: no-blocking-collision release check Co-authored-by: Zwitschi --- Docs/release-checks.md | 25 +++++++- .../Source/GigLine/GigLineCharacter.cpp | 60 ++++++++++++++++++- .../Source/GigLine/GigLineShotActor.cpp | 8 +++ MEMORY.md | 15 +++++ TODO.md | 4 +- 5 files changed, 106 insertions(+), 6 deletions(-) diff --git a/Docs/release-checks.md b/Docs/release-checks.md index bf80f32..e3b3e9c 100644 --- a/Docs/release-checks.md +++ b/Docs/release-checks.md @@ -38,7 +38,7 @@ Measured at 1440p on the development workstation: Bazzite, Ryzen 7 7700X, 64 GB | ------- | ----------------------- | ------ | -------- | ---- | | `0.1.0` | Traversal, both sides | — | — | — | | `0.1.0` | Placement vs references | PASS (with deliberate simplifications) | Section below, 2026-11-08 | 2026-11-08 | -| `0.1.0` | Collision and packaging | — | — | — | +| `0.1.0` | Collision and packaging | PASS — no blocking collision, no invisible walls, no seam gaps (see audit note) | `-qaselftest` on the packaged build, 2026-11-08 | 2026-11-08 | | `0.1.0` | 60 FPS at 1440p | — | — | — | | `0.1.0` | Fresh checkout build | — | — | — | | `0.1.0` | Packaged Linux build | — | — | — | @@ -83,3 +83,26 @@ probed from the DemoMap (commandlet, 2026-11-08). Pre-check method: `Tools/judge_screenshot.py` (vision model) flagged the simplified flat surfaces as "unexpected" — those flags are the deliberate deviations above, not placement errors. Final verdict is by human review of the shots in `Screenshots/demo/`. + +## `0.1.0` — Collision and packaging — audit note (2026-11-08) + +Run on the packaged build (`GigLineQA`, Development, Vulkan SM5) with `-qaselftest`. + +- **Transect (holes/gaps):** 1456 samples over 16 rows — lane centres **plus the + kerb/road/sidewalk seam Ys** (−1382, −1053, +718, +1047 and neighbours) and + X −20000..−2000. Result **0 holes**. (The old 84-sample check covered only 3 + lane centres; the seam rows are what this check adds.) +- **Invisible-wall probe:** horizontal traces 30 m long, grounded at 160 cm above + the local ground, along the road and both seam rows. Result **0/35 blocking hits + inside 21 m** — no invisible walls or misplaced blockers in the street envelope. + (First pass used a fixed Z 160 start; the street is at Z ~366, so the probe + started inside the earth and reported every row as a dist-0 blocker. Grounding + the probe fixed that measurement bug.) +- **Walk / reset / pause:** 761 cm in 2 s (Z steady 456), reset 0 cm, pause + `IsGamePaused()=1`, unpause 0 — all PASS. +- Packaging: the packaged build launches, reaches gameplay, and the QA run quits + cleanly. The `GigLineShotActor` screenshot rounds are suppressed under + `-qaselftest` so they cannot quit the run before the audit phases. +- Caveat recorded: ground Z extremes (−12 m / 3000 cm) are trace-start/terrain-edge + artifacts of the widened extent, not walkable holes — the walkable corridor is + continuous (walk + transect both PASS). diff --git a/Game/GigLine/Source/GigLine/GigLineCharacter.cpp b/Game/GigLine/Source/GigLine/GigLineCharacter.cpp index 7f41a72..1b0705a 100644 --- a/Game/GigLine/Source/GigLine/GigLineCharacter.cpp +++ b/Game/GigLine/Source/GigLine/GigLineCharacter.cpp @@ -266,10 +266,18 @@ void AGigLineCharacter::StartQASelfTest() UWorld* W = GetWorld(); int32 Holes = 0, Samples = 0; float Lowest = 1.0e9f, Highest = -1.0e9f; - static const float Lanes[] = {-1400.0f, -167.0f, 1000.0f}; // N walk, road, S walk - for (float LaneY : Lanes) + // Probed world-space (2026-11-08): N sidewalk -1912..-1053, kerb + // -1382..-1053, road -1382..+1047, kerb +718..+1047, S sidewalk + // +718..+1447. Rows include the kerb/road/sidewalk boundary Ys so + // seam gaps are sampled, not just lane centres. + static const float Rows[] = { + -1750.0f, -1500.0f, -1382.0f, -1280.0f, -1053.0f, -900.0f, + -167.0f, 0.0f, 300.0f, 800.0f, 1047.0f, 900.0f, 718.0f, + 900.0f, 1250.0f, 1447.0f + }; + for (float LaneY : Rows) { - for (float X = -14900.0f; X <= -9420.0f; X += 200.0f) + for (float X = -20000.0f; X <= -2000.0f; X += 200.0f) { FHitResult Hit; FCollisionQueryParams Q(TEXT("GigLineTransect"), false, this); @@ -289,6 +297,52 @@ void AGigLineCharacter::StartQASelfTest() UE_LOG(LogTemp, Log, TEXT("GigLine QA: transect %d samples, %d holes, ground Z %.0f..%.0f -> %s"), Samples, Holes, Lowest, Highest, Holes == 0 ? TEXT("PASS") : TEXT("FAIL")); + + // Invisible-wall / blocking-mesh probe: at each sample, first find + // the ground under the start point (vertical raycast), then run a + // horizontal trace 160 cm ABOVE that ground along the street row. + // A clean street should let the 30 m trace run clear; a short hit + // flags an invisible or misplaced blocker. Starting the horizontal + // trace at a fixed Z (e.g. 160) is wrong: the street is at Z ~366 + // there, so a 160 cm probe starts inside the earth (dist 0). + int32 Walls = 0, WallSamples = 0; + static const float TraceYs[] = {-1400.0f, -167.0f, 1000.0f, -1382.0f, 1047.0f}; + for (float WallY : TraceYs) + { + for (float X0 = -19500.0f; X0 <= -3000.0f; X0 += 2500.0f) + { + FHitResult GroundHit; + FCollisionQueryParams GQ(TEXT("GigLineWallGround"), false, this); + if (!W->LineTraceSingleByChannel(GroundHit, + FVector(X0, WallY, 3000.f), FVector(X0, WallY, -1000.f), + ECC_Visibility, GQ)) + { + continue; // no ground under start -> skip + } + const float ProbeZ = GroundHit.ImpactPoint.Z + 160.f; + FHitResult Hit; + FCollisionQueryParams Q(TEXT("GigLineWallProbe"), false, this); + const bool bHit = W->LineTraceSingleByChannel(Hit, + FVector(X0, WallY, ProbeZ), + FVector(X0 + 3000.f, WallY, ProbeZ), + ECC_Visibility, Q); + ++WallSamples; + if (!bHit) { continue; } // 30 m clear -> good + // Starting just above the ground, a hit within 21 m = blocker. + if (Hit.Distance < 2100.f) + { + ++Walls; + UE_LOG(LogTemp, Log, + TEXT("GigLine QA: wall probe short-hit at X %.0f Y %.0f dist %.0f actor=%s"), + X0, WallY, Hit.Distance, + Hit.GetActor() ? *Hit.GetActor()->GetName() : TEXT("none")); + } + } + } + UE_LOG(LogTemp, Log, + TEXT("GigLine QA: wall probe %d/%d traces with blocking hit inside 21 m -> %s"), + Walls, WallSamples, + Walls == 0 ? TEXT("PASS") : TEXT("FAIL")); } // 7.0: verdict, then stop ticking. diff --git a/Game/GigLine/Source/GigLine/GigLineShotActor.cpp b/Game/GigLine/Source/GigLine/GigLineShotActor.cpp index d8d819a..ba0cc15 100644 --- a/Game/GigLine/Source/GigLine/GigLineShotActor.cpp +++ b/Game/GigLine/Source/GigLine/GigLineShotActor.cpp @@ -21,6 +21,14 @@ void AGigLineShotActor::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds); + // QA mode: the -qaselftest run owns the process (it walks, resets, pauses, + // transects, then quits on its own). Skip the screenshot rounds so the game + // does not exit mid-test via bQuitWhenDone. + if (FParse::Param(FCommandLine::Get(), TEXT("qaselftest"))) + { + return; + } + if (bWaitingForSettle) { SettleCountdown -= DeltaSeconds; diff --git a/MEMORY.md b/MEMORY.md index 83ab3ab..5cbea1d 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -304,3 +304,18 @@ Scope: facts and traps that cost time to establish. Planned work lives in [TODO. - Getting actor world bounds: spawn at loc(0,0,0) means static mesh bbox IS the world footprint (FBX carries the geo offset). Probe by loading the level explicitly (load_level) — a fresh commandlet starts on Untitled otherwise. + +## 2026-11-08 — collision/seam audit done; QA self-test extended + +- `-qaselftest` transect widened from 84 samples (3 lane centres) to **1456 + samples / 16 rows** including kerb/road/sidewalk seam Ys (probed: N side + -1912..-1053, kerb -1382..-1053, road -1382..+1047, kerb +718..+1047, S side + +718..+1447). 0 holes. +- **New wall probe:** ground-referenced horizontal 30 m trace at +160 cm. First + attempt used a fixed Z 160 and started INSIDE the earth (street is at Z ~366) + -> every row reported a dist-0 blocker. Always ground the probe first. +- **Guard:** GigLineShotActor must skip rounds under `-qaselftest`, else + bQuitWhenDone closes the game before the transect phase (observed: game exited + at t=2s with "ShotActor: done (4 shots)" + RequestExit). +- Results on packaged build: walk 761cm/2s, reset 0cm, pause/unpause, transect + 0 holes, wall 0/35 -> all PASS. Evidence recorded in Docs/release-checks.md. diff --git a/TODO.md b/TODO.md index 78b6ee4..5d7035f 100644 --- a/TODO.md +++ b/TODO.md @@ -106,11 +106,11 @@ The MPFB character asset is planned as the NPC template (see `ROADMAP.md`). Keep - [x] Commit and review the publication preparation changes before pushing. Initial publication commit: `101db3f`. - [x] Review ODbL obligations for each shipped derivative database or produced work before public release. **Done 2026-11-08** — full review recorded in `Docs/odbl-review.md`: the shipped streets/footprints/sidewalks are a Produced Work of the OpenStreetMap database; obligations held are on-screen attribution (`AGigLineHUD`), the ODbL link, and the committed source extract with provenance; share-alike applies to the data layer only (project code/art stay CC0). THIRD_PARTY_NOTICES and `Docs/licensing.md` updated. -### Release checks - active - 01a1b26b-3c94-707f-9665-2f6b4a79b564 (branch `placement-reference-0.1.0`) +### Release checks - active - 01a1b26b-3c94-707f-9665-2f6b4a79b564 (branch `collision-seams-0.1.0`) - [x] Player can traverse both sides of the demo without falling through geometry. **Verified 2026-11-08:** Blender pre-check plus an in-engine swept transect (`-qaselftest`) over the north sidewalk, carriageway and south sidewalk from X -14900 to -9420 — 84 samples, 0 holes, ground Z 317..395 (sidewalk 395, road 366, south walk 317). The player spawns at Z 456 and stays there for the whole run (`fall=0`, single Z). - [x] Building placement, street scale, and site identity checked against references. **Done 2026-11-08:** rendered current DemoMap content, probed world-space geometry (road 24.3 m vs ref 20.9–25.9 m; sidewalk centreline offsets 12.5/13.2 vs ref 12.6/13.6 m; hero frontage 26.0 m, setback ~13 m vs ref 25.75/14.5 m; frontage + north buildings in verified OSM positions), recorded PASS + deliberate deviations (flat blockout surfaces → `0.1.4`, kerb upstand → `0.1.1`, kerb-flush ramps → `0.1.1`) in `Docs/release-checks.md`, and fixed the mis-aimed north-buildings camera (was 9.5 km west → 85% sky; now frames the buildings). Final human verdict pending on `Screenshots/demo/*`. -- [ ] No blocking collision or packaging defects. +- [x] No blocking collision or packaging defects. **Done 2026-11-08:** extended `-qaselftest` — transect now 1456 samples / 16 rows including the kerb/road/sidewalk seam Ys (0 holes), plus a ground-referenced 30 m wall probe (0/35 blocking hits; no invisible walls). Walk/reset/pause all PASS on the packaged build; `GigLineShotActor` now skips under `-qaselftest`. Evidence in `Docs/release-checks.md` §`0.1.0` audit note. - [ ] Remove TEMP QA diagnostics (`GigLine SIM` / `GigLine GROUND` log lines and the settle-timer log) from `GigLineCharacter.cpp` before the release tag, or gate them behind a cvar. - [ ] 60 FPS at 1440p on the dev workstation (Bazzite, Ryzen 7 7700X, 64 GB RAM, RTX 5070 Ti 16 GB) using a documented quality preset; record upscaling state; report performance without frame generation. - [ ] Fresh project checkout builds with the documented external engine installation.