Compare commits
35
Commits
v0.1.3
..
bc2e9c3b8e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc2e9c3b8e | ||
|
|
12272d6b96 | ||
|
|
56e104f5f3 | ||
|
|
8e880ed09d | ||
|
|
9ce7011389 | ||
|
|
f5e7e66127 | ||
|
|
dbb177da6f | ||
|
|
eeeabe56b1 | ||
|
|
f84093adba | ||
|
|
0e0aed02d6 | ||
|
|
7ea3c1999c | ||
|
|
46b84884b8 | ||
|
|
8993fd83c4 | ||
|
|
a1f5989cbb | ||
|
|
d0f94dff4e | ||
|
|
50e023f15d | ||
|
|
e487655e2f | ||
|
|
304c1e9afd | ||
|
|
05c1488c00 | ||
|
|
96238bdb6a | ||
|
|
817313aabd | ||
|
|
744bf8f357 | ||
|
|
c72bc41af1 | ||
|
|
803e77473c | ||
|
|
3942e9a215 | ||
|
|
4132f42ca1 | ||
|
|
c53386fcf4 | ||
|
|
2614d259aa | ||
|
|
4e703fcfd9 | ||
|
|
73aca9db80 | ||
|
|
743a290ac9 | ||
|
|
71c7ca734b | ||
|
|
b8209ce82b | ||
|
|
a1d52aa583 | ||
|
|
5e2c5e29c0 |
No files matched your search
@@ -47,6 +47,10 @@ Thumbs.db
|
||||
/Geo/raw/mapbox-cache/
|
||||
/Geo/verification/*mapbox*
|
||||
|
||||
# Visual-loop iterations: generated comparisons stay uncommitted until the owner
|
||||
# confirms a shot. Only Screenshots/ is committed.
|
||||
/Art/reference/iterations/
|
||||
|
||||
# Local, private reference material — never committed (see Docs/reference-notes-hollywood.md)
|
||||
/Art/reference/
|
||||
/Docs/data-inventory.md
|
||||
|
||||
@@ -67,6 +67,7 @@ Rules for `TODO.md`:
|
||||
- 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.
|
||||
- **Realism leads, performance is a floor.** When a realism improvement costs frame time, realism wins and the cost is paid — but 60 FPS at 1440p without frame generation must still be met at every release. If realism cannot fit the budget, reduce the realism scope deliberately and record it in `CHANGELOG.md`; never ship below the floor without the owner's approval. Walkability outranks realism in one direction only: a block the player falls through is not a demo.
|
||||
|
||||
## Git workflow
|
||||
|
||||
@@ -209,8 +210,8 @@ 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. Optional pre-check with a vision model: `Tools/judge_screenshot.py --image <shot.png> --expect "<what it should show>"` (or `--shots` to run every shot in the README) flags likely-missing content before a human looks. It reads `OPENROUTER_API_KEY` from `.env` (ignored). Small helper only — it never replaces the human verdict.
|
||||
3. 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. Update `Screenshots/README.md` **in the same change as any visual work** — every change that affects appearance, not only new shots: the image is **embedded inline** (``) with its view, expected content, verification and approval state, plus one row in the status table at the top. The review happens in the file; a path alone is not a review, and a shot whose README section is stale is a defect.
|
||||
4. 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.
|
||||
5. Wait for the user's approval before treating a visual change as done; record approval by ticking the table in `Screenshots/README.md`.
|
||||
5. Wait for the user's approval before treating a visual change as done; record approval by ticking both the shot's section and its row in the status table 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`.
|
||||
Binary file not shown.
Binary file not shown.
@@ -37,6 +37,15 @@
|
||||
],
|
||||
"evidence": "6 columns at 4.96 m centres (sibling notes x measured frontage)"
|
||||
},
|
||||
{
|
||||
"object": "Hero_Flute_x120",
|
||||
"dims_m": [
|
||||
0.064,
|
||||
0.064,
|
||||
6.53
|
||||
],
|
||||
"evidence": "photographs: fluted shafts; 20 grooves per column modelled as shallow cylinders in the shadow tone rather than boolean cut grooves"
|
||||
},
|
||||
{
|
||||
"object": "Hero_ColumnBase_x6",
|
||||
"dims_m": [
|
||||
@@ -104,10 +113,10 @@
|
||||
"object": "Hero_MottoBand",
|
||||
"dims_m": [
|
||||
22.75,
|
||||
0.08,
|
||||
0.1,
|
||||
0.85
|
||||
],
|
||||
"evidence": "photographs: motto panel above the colonnade"
|
||||
"evidence": "photographs: motto panel above the colonnade, now an inset panel with a moulded surround rather than a flat stripe"
|
||||
},
|
||||
{
|
||||
"object": "Hero_Medallion_x2",
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# Reference analysis — retail frontage, 0.1.6
|
||||
|
||||
Phase 0 of the `references` pack (reference-image-match). Written before any geometry, because the pass that produced the hero façade skipped it and cost two rebuilds.
|
||||
|
||||
## Source
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
| Image | `Art/reference/retail-neighbours/01-lee-drug-and-neighbor.jpg` (1920×953) |
|
||||
| Author / licence | Gb321, own work, **CC BY-SA 4.0** — attribution required |
|
||||
| Captured | 2024-07-19 11:11 local |
|
||||
| Description | "Lee Drug and 6806 Hollywood Buildings in Hollywood" |
|
||||
| Coverage | Both target buildings in one frame, mid-morning sun from the south-east |
|
||||
|
||||
**One photograph is the whole evidence base for 6800 and 6806–6820.** It is a street-level oblique, not an elevation: verticals converge, the ground floor of 6806–6820 is partly behind a parked car, and the Lee Drug shopfront is behind a bus. Everything below that the photo does not resolve is marked _assumed_ and is carried into the part report.
|
||||
|
||||
**6822 Budget has no photograph.** It is a 6.0 m × 5.5 m single-storey sliver west of 6806–6820, and the TODO item already records that Commons has nothing usable. It stays a blockout extrusion and the item is deferred rather than guessed.
|
||||
|
||||
## Measured (not from the photo)
|
||||
|
||||
From `Geo/raw/osm-api-map-2026-11-06.osm` and the blockout, in the shared frame (metres, X east, Y north, Z from the project datum):
|
||||
|
||||
| Building | OSM way | X | Front face Y | Height | Depth |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 6800 Lee Drug | 424024149 | −33.20 … −14.37 | −14.63 | 12.10 m | 33.5 m |
|
||||
| 6806–6820 | 424024150 | −66.08 … −33.07 | −12.52 | 12.10 m | 30.8 m |
|
||||
| 6822 Budget | 424024144 | −78.54 … −72.52 | −12.92 | 5.50 m | 15.4 m |
|
||||
|
||||
Both target buildings are 12.10 m tall, which makes the _relative_ proportions in the photo directly usable: the parapet line, the cornice and the shopfront line can be read as fractions of the frame height.
|
||||
|
||||
## 6800 Lee Drug — reading
|
||||
|
||||
| Category | Observation |
|
||||
| --- | --- |
|
||||
| Subject | Corner retail block at Hollywood × Highland; the street face and the corner return are both visible |
|
||||
| Silhouette | Long low box, broken by a **rounded corner tower** that rises above the frontage parapet; the parapet is **stepped**, not flat |
|
||||
| Storeys | Two zones, not storeys: a tall shopfront zone and a single tall display wall above it |
|
||||
| Proportion (vertical) | Shopfront ≈ 0 → 0.36 H; bronze cornice ≈ 0.36 → 0.43 H; mural + lettering wall ≈ 0.43 → 0.82 H; stepped parapet ≈ 0.82 → 1.0 H (_assumed split; read off the frame, ±0.03 H_) |
|
||||
| Proportion (horizontal) | Corner tower occupies ≈ 4 m of the east end; eight mural panels at roughly 2.2 m centres along the frontage |
|
||||
| Colour | Cream stucco wall `#EDE6D8`, bronze/brown cornice `#6B4A2F`, near-black mural plates `#1C1A18`, dark glazing `#20262A` (_sampled from the frame_) |
|
||||
| Unique features | Stepped Deco parapet with 4 horizontal reveal lines; projecting bronze cornice; row of black celebrity-portrait panels; large dark "SOUVENIRS OF …" lettering on the upper wall; shopfront glazing with slim mullions |
|
||||
| Mood | 1930s Deco retail, freshly painted, bright midday |
|
||||
|
||||
## 6806–6820 — reading
|
||||
|
||||
| Category | Observation |
|
||||
| --- | --- |
|
||||
| Silhouette | Flat-fronted two-storey block; parapet steps up in fins at the west end |
|
||||
| Proportion (vertical) | Shopfront ≈ 0 → 0.32 H; burgundy fascia band ≈ 0.32 → 0.38 H; window storey ≈ 0.38 → 0.62 H; blank wall + parapet ≈ 0.62 → 1.0 H (_assumed_) |
|
||||
| Horizontal | Brown pilaster strips divide the frontage into roughly 3 m bays; seven upper windows across 33 m |
|
||||
| Colour | Same cream stucco; brown pilasters `#6B4A2F`; **burgundy-green** fascia `#5B3A3A`; white window frames |
|
||||
| Unique features | Banded parapet with 3 reveal lines; white-framed upper windows with security bars; shopfront glazing behind a burgundy fascia |
|
||||
|
||||
## Camera match (for the review shot)
|
||||
|
||||
The photo is a street-level oblique from the **north** side looking south-east, subject filling ~80 % of the frame width. The demo review shot must reproduce that: camera at 1.7 m on the north sidewalk, yaw toward the south frontage, 35–42° lens, both buildings in frame. A wide block view will not compare usefully against this crop.
|
||||
|
||||
## Assumptions to carry into the model report
|
||||
|
||||
1. Vertical proportions, as above — the photo's convergence makes a pixel-perfect read impossible.
|
||||
2. Mural panel size and spacing; the lettering is modelled as a recessed plate, not as text.
|
||||
3. Shopfront bay spacing on both buildings; the bus hides most of Lee Drug's ground floor.
|
||||
4. Parapet reveal line count (4 on Lee Drug, 3 on 6806–6820).
|
||||
5. Corner tower radius; only the outer curve is visible.
|
||||
+160
-1
@@ -8,6 +8,163 @@ Format: [Keep a Changelog](https://keepachangelog.com/) style, versions follow [
|
||||
|
||||
Nothing yet.
|
||||
|
||||
## [0.2.0] — 2026-11-09
|
||||
|
||||
Third-person body. The pawn gains a visible, animated body and a third-person camera, so the walk cycle is finally something a player can see rather than a clip in an asset list.
|
||||
|
||||
### Added
|
||||
|
||||
- **`Tools/character_anim_build.py`**: `GigLine_Idle` (60 frames) and `GigLine_Walk` (30 frames) at 30 fps, authored as parametric cycles so the loop closes by construction. Limb axes were **measured** on the rig — a probe rotated `upperleg01.L` by 0.5 rad on each axis and read `foot.L`, which showed X swings the limb fore/aft while Y twists and Z abducts.
|
||||
- **Third-person rig** (`ADR-9`): a visible skeletal body, a `USpringArmComponent` with collision testing, and a **V** toggle. One pawn carries both rigs; first person stays the shipped default.
|
||||
- **Walk capture harness**: a per-shot `ShotFollow` mode that re-places the review camera every frame in the pawn's frame, plus `SetReviewWalk` / `ReturnToStartForReview`, so a stride is captured deterministically and the fixed-camera shots still find the pawn at spawn.
|
||||
- **Review shots 6, 7 and 8** — third-person back, front and mid-stride — embedded in `Screenshots/README.md`.
|
||||
|
||||
### Changed
|
||||
|
||||
- Character export preset now **bakes animation** (`bake_anim`, all actions). Without it the exporter wrote a static bind pose and Unreal imported a mesh with no clips and no error.
|
||||
- `Docs/controls.md` documents the V toggle and the review switches; the in-game HUD line reads `WASD move · Mouse look · Space jump · V view · R reset · Esc pause`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **`PlayAnimation` on a hidden component never took effect.** The body is hidden at construction and only unhidden by the toggle, so the first call could land before the component was ready; the code now sets `AnimationSingleNode`, calls `SetAnimation` + `Play`, and re-asserts while the instance is missing.
|
||||
- **The animation switch restarted the clip every frame**, freezing the body on frame 0.
|
||||
- **The importer silently refused to replace the existing assets.** Changing the arm rotation and rebuilding returned the identical bone quaternion every time; `replace_existing` and `delete_asset` both failed because the pawn's constructor hard-references the clips. Importing under a new name (`CharacterV2`) finally landed the change — that is what unblocked the pose after a dozen failed rebuilds.
|
||||
- **The review runner copied stale screenshots** from `Saved/`, so a shot retired from the camera list reappeared with byte-identical content from an older run. The folder is cleared before each capture.
|
||||
- **Walking was never enabled for the stride shot**: the call sat in the shot sequencer's `BeginPlay`, which runs before the player pawn exists, so the cast silently found nothing and `speed` stayed at 0.
|
||||
|
||||
### Verified
|
||||
|
||||
- **8 review shots verify**; shots 6, 7 and 8 approved by the owner.
|
||||
- **1440p, no frame generation, VSync off**: 759 frames, avg **2.64 ms (379 fps)**, p50 2.54, p95 3.39, p99 3.76, max 4.05 ms — about 2 % over `0.1.5` and 6.3× the 60 fps floor. Series: `Docs/perf/0.2.0-walk-frametimes.csv`.
|
||||
- `-qaselftest`: walk, reset 0 cm from spawn, pause/unpause, transect 1456 samples / 0 holes, wall probe 0/35, façade probe 0/9 — all PASS.
|
||||
- 82 tool tests pass.
|
||||
|
||||
### Known limitations
|
||||
|
||||
- **The fingers splay** — the bind's finger spread carries into the pose. Deferred to `0.2.2`.
|
||||
- **The arms-down rotation was found by sweeping**, not derived: 0.55 rad puts the arms at the sides, 0.65 against the hip, 0.75 into the torso. A derived pose is a `0.2.2` job.
|
||||
- **Camera collision is implemented but unproven** — the spawn point has open space behind it, so no shot exercises the boom shortening.
|
||||
- The third-person body has no head or hand IK and no turn-in-place; it is a first playable, not a finished character.
|
||||
|
||||
## [0.1.6] — 2026-11-09
|
||||
|
||||
Retail frontage. The two retail buildings beside the hero are modelled from their own photograph instead of the generic kit, and the photograph is treated as evidence: an analysis is written before any geometry, and every dimension the image does not resolve is carried into the part report as an assumption.
|
||||
|
||||
### Added
|
||||
|
||||
- **`Tools/retail_frontage_build.py`**: 6800 Lee Drug and 6806–6820 built from the one usable reference (`Art/reference/retail-neighbours/`, Gb321, CC BY-SA 4.0). Lee Drug gets a bronze cornice, storefront glazing with mullions, framed portrait panels, a painted lettering band, two-tier parapet with reveals and a fluted corner tower; 6806–6820 gets a burgundy fascia, brown pilasters, white-framed windows with security bars and its own banded parapet. Built in world coordinates from the blockout bounds (Lee Drug X −33.20…−14.37, face Y −14.63, H 12.10 m; 6806–6820 X −66.08…−33.07), joined to one mesh per building with a JSON part report.
|
||||
- **`Tools/ue_import_retail_detail.py`**: imports the two meshes with complex-as-simple collision — trim decorates a solid the player already collides with and must not add snag points.
|
||||
- **`Art/blender/retail/ref-analysis.md`**: the reference analysis the `references` skill pack requires, written before modelling.
|
||||
- **`Tools/_common.py`** and `Tools/tests/test_retail_frontage.py`: the bay-layout arithmetic, extracted so it is testable without Blender (4 tests).
|
||||
- **Review shot 5**, `demo/review-demo-retail-frontage`, embedded in `Screenshots/README.md`.
|
||||
|
||||
### Changed
|
||||
|
||||
- **The generic façade kit skips the two modelled buildings** (`MODELLED` list in `ue_apply_facade_kit.py`). It applies to every `Frontage_*`/`North_*` actor, so it was pasting a second, contradicting façade language over the new trim.
|
||||
- **Retail trim stages as `RetailTrim_*`** and is spawned through the demo builder, so it is regenerable rather than hand-placed.
|
||||
- Trim stucco, white and glass slots are mapped onto the demo's look-dev materials so the walls sit in the same noise and texture family as the rest of the block; the tones sampled from the photograph (bronze, burgundy, mural, lettering) stay as authored.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **`Tools/render_screenshots.sh` never captured anything.** The capture step called `./Engine/...` while every commandlet above it went through `run_cmdlet`'s `cd "$ENGINE"`. Under `set -e` the script died at exit 127 _after_ rebuilding the maps, so the PNGs silently stayed a day old and `--verify` never ran.
|
||||
- **Duplicate `RetailTrim_*` actors on every rebuild**: the demo builder's clear list did not include the new label, so each run stacked another copy. Caught by the scene audit, not by eye.
|
||||
- **Mural plates were placed 0.20 m inside the solid blockout volume** and never rendered; the inset is now faked with a proud frame and a near-flush plate.
|
||||
- **Trim detail sat 0.05 m off the wall**, under a pixel of depth at 40 m, so the wall won the depth test. Trim planes now stand 0.10–0.20 m proud.
|
||||
- **6806–6820 parapet started at 0.62 H**, leaving a 4.6 m blank band; the window storey and parapet moved up to follow the photograph.
|
||||
- **Mural panels, parapet and lettering were sized from the photograph's proportions, not the inherited defaults**: the first pass left a parapet that read as one blank white band.
|
||||
|
||||
### Verified
|
||||
|
||||
- `render_screenshots.sh --verify`: **5/5 shots OK**; scene audit **0 failures** (every mesh materialed, all spans in tolerance, no duplicate labels).
|
||||
- Owner approved `demo-retail-frontage` (second pass) 2026-11-09; all five demo shots now carry approval.
|
||||
- 82 tool tests pass.
|
||||
|
||||
### Known limitations
|
||||
|
||||
- **Wall lettering and the painted band do not read at 40 m.** A plate at 0.10 m still vanishes; this is a decal/texture problem, not geometry. Moved to `0.9.0 — Realistic buildings`.
|
||||
- **The parapet has no moulded profile** and the corner tower is a fluted cylinder with two cap rings — both need the façade system in `0.9.0`.
|
||||
- **6822 Budget is untouched**: no photograph of it exists. Deferred and blocked in `0.9.0`.
|
||||
- The only reference for these two buildings is a single street-level oblique with a bus across Lee Drug's shopfront and a car across 6806's; several vertical proportions are assumptions recorded in the part report.
|
||||
|
||||
## [0.1.5] — 2026-11-09
|
||||
|
||||
Façade finishing. The kit gets the hero detail it was missing, and a class of silent defect is found and gated: the map had been rendering with **604 of 620 mesh actors unmaterialed** without a single error.
|
||||
|
||||
### Added
|
||||
|
||||
- **Scene audit** (`Tools/ue_audit_scene.py`): fails when any staged mesh has no material, when a frontage building is more than ±1 m off its verified footprint, or when two actors share a label. Wired into `Tools/render_screenshots.sh`, which now runs the whole staging chain in the correct order and stops on an audit failure.
|
||||
- **`Tools/ue_placement.py`**: one source of truth for staged positions and the verified frontage spans, with `span_error` / `describe_span_failure` validators.
|
||||
- **Hero fluting**: 20 grooves per column as shallow shadow-tone cylinders (120 pieces) rather than boolean cuts.
|
||||
- **Presence of references for the retail run**: `Art/reference/retail-neighbours/` (6800 Lee Drug and its neighbour). Local only, not committed.
|
||||
|
||||
### Changed
|
||||
|
||||
- **Motto band is a real recess** now — moulded surround 6 cm proud, panel 12 cm behind — instead of an 8 cm dark stripe. Hero: 148 parts / 3 044 tris → 271 / 5 492, still 14.50 m.
|
||||
- **Kit layout for wide and tall buildings**: bays are capped at 9 with the module widened (6922 was 13 bays across 65.8 m — a window every 4.96 m on a 37 m block), and the storey spread is derived from the available height so the top band always lands inside the volume.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **604 of 620 mesh actors rendered untextured.** `ensure_material` deleted and recreated each site material on every run, orphaning every reference held by the kit pieces and the theatre trim; the map looked only slightly flat. Materials are now updated in place.
|
||||
- **The hero could be teleported 13 m east onto the wrong side of the boulevard.** `ue_hero_facade_import.py` carried its own copy of the pre-`0.1.0` hero position; running the importer alone moved the building, invisible in a render and visible only as a reference-score drop. Placement now has one definition and the import asserts the staged span (`span ok: X -14644..-12028`).
|
||||
- **53 duplicated kit actor labels** (both shop piers of a bay shared one name).
|
||||
|
||||
### Verified
|
||||
|
||||
- Scene audit: **0 failures** — every mesh materialed, all 7 verified spans within tolerance, no duplicate labels.
|
||||
- Packaged `-qaselftest`: walk, reset, pause/unpause, seam transect 1456 samples / 0 holes, wall probe 0/35, façade probe 0/9 — all PASS.
|
||||
- **1440p re-measured with the kit**: 2.76 ms average (**363 fps**), p99 3.64 ms, against `0.1.0`'s 2.42 ms (414 fps) — the 625 kit actors and 8 textures cost about 12 % and leave roughly six times the 60 fps floor. Series at `Docs/perf/0.1.5-kit-walk-frametimes.csv`.
|
||||
- 78 tool tests pass.
|
||||
|
||||
### Known limitations
|
||||
|
||||
- **Retail neighbours 6822 and 6806–6820 are not modelled**: Commons holds no usable reference for either, and substituting the El Capitan Theatre's photographs would misattribute their façades. Tracked in `TODO.md`.
|
||||
- Cornice dentil spacing, abacus and plinth dimensions remain recorded assumptions.
|
||||
- Textures are generated, not authored — no decals, signage text or weathering maps.
|
||||
- The vision pre-check stays advisory; it has rejected shots the owner approved.
|
||||
|
||||
## [0.1.4] — 2026-11-09
|
||||
|
||||
Building façades. The block goes from flat extruded boxes with flat colours to coursed, textured surfaces assembled from a reusable module kit, and the El Capitan Theatre gets the marquee and blade sign that make it read as itself.
|
||||
|
||||
### Added
|
||||
|
||||
- **Façade module kit** at the measured **4.96 m bay** / **3.10 m storey**: pilaster, sill, lintel, window bay, shopfront bay, door bay, cornice, parapet and coping — 16 `SM_` meshes, each validated for manifold topology (12 tris each) and assembled onto **eight buildings, 625 pieces**, from each building's own world bounds.
|
||||
- **Generated surface textures** — deterministic stdlib PNGs for ashlar stone, stucco, asphalt and concrete (base colour + roughness), sampled world-aligned with per-material tiling; byte-identical on re-run.
|
||||
- **Materials**: masonry coursing and roughness variation in the material graphs; `M_FacadeStoneShadow`, `M_FacadeGlass` (specular 0.62 ≈ IOR 1.5) and `M_FacadeBanner`. Material assignment is now **per slot**, keyed on the imported slot material, instead of slot 0 only.
|
||||
- **El Capitan Theatre trim**: projecting marquee (12.0 × 2.60 m canopy at a 4.60 m soffit), illuminated fascia, recessed soffit, and a vertical blade sign (1.30 × 5.20 m) with frame and wall bracket.
|
||||
- **Visual review loop** (`Tools/visual_loop.py` + `visual_targets.json`): verifies every capture against an expectation (missing, blank, tone-less or wrong-size shots fail) and measures each render against its reference photograph, emitting feedback that names the discipline to consult. Iterations are written under `Art/reference/iterations/`, which is gitignored until a shot is confirmed.
|
||||
- **`Tools/fbx_export_preset.py`** — one export preset replacing six copies, and it exports smoothing groups (`FACE`), so round geometry is no longer faceted.
|
||||
- **`Tools/ue_common.py`** — clears the read-only bit an LFS `.umap` checks out with and makes a refused save loud.
|
||||
|
||||
### Changed
|
||||
|
||||
- **Elevation cameras** are levelled (pitch 0, so verticals stay parallel) and use a 42–55° lens to fill the frame from across the street.
|
||||
- **`Screenshots/` holds demo-map captures only**; the review staging map is no longer rendered for review. Each shot carries its expected content and an approval state.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Blockout frame**: the `0.1.2` regeneration had run without `--origin`, shifting the whole frontage ≈119 m in X and 2.4 m in Y and dropping the hero out of the 6902/6838 gap. Regenerated with the affine fit (residual 0.0 cm) and every span re-verified against the block verification notes.
|
||||
- **Kit placed ~12 km from its buildings** — it used the mesh-local bounding box, which excludes the actor offset.
|
||||
- **Marquee mirrored across the boulevard** (Blender +Y is Unreal −Y) and **hanging 5 cm above the pavement** (absolute Z instead of pavement-relative).
|
||||
- **Window bays invisible** inside the solid wall; the surround now projects with glazing inset.
|
||||
- **Glazing was not glass**: kit pieces kept the grey material the FBX import creates.
|
||||
- **Hero windows floated 1.44 m in front of the wall**; parapet medallions were flat discs on the coping.
|
||||
- **Imports could report success while saving nothing** (read-only LFS map), and the north-buildings import failed silently on a missing `import sys`.
|
||||
|
||||
### Verified
|
||||
|
||||
- Packaged build `-qaselftest`: walk 761 cm, reset, pause/unpause, seam transect 1456 samples / 0 holes, wall probe 0/35, façade occlusion probe 0/9 — all PASS. Run twice; the harness is time-based and one loaded run reported a false failure.
|
||||
- All eight buildings probed against the verified world spans.
|
||||
- 67 tool tests pass.
|
||||
|
||||
### Known limitations
|
||||
|
||||
- Kit pieces are boxes; cornice dentil spacing, abacus and plinth dimensions are recorded assumptions.
|
||||
- Textures are generated, not authored: no decals, signage text or weathering maps.
|
||||
- The vision pre-check remains advisory — it is unreliable on this content.
|
||||
- Retail neighbours, hero fluting and the blockout cleaning pass move to `0.1.5`.
|
||||
- Performance has not been re-measured since `0.1.0`; 625 kit actors are unmeasured.
|
||||
|
||||
## [0.1.3] — 2026-11-09
|
||||
|
||||
Packaging and setup fixes. No gameplay or visual scope; this patch makes the pipeline and its documentation tell the truth about what `0.1.x` taught.
|
||||
@@ -129,7 +286,9 @@ The first playable release: a walkable exterior demo of the **6840 Hollywood Blv
|
||||
- Kerb height (0.13 m as built vs 0.15 m assumed), kerb radii, driveway aprons and graded crossing ramps are deferred to `0.1.1`; crossing ramps are kerb flushes.
|
||||
- One block only; no vehicles, NPCs, audio, or settings UI yet.
|
||||
|
||||
[Unreleased]: https://git.allucanget.biz/zwitschi/gig-line/compare/v0.1.3...main
|
||||
[Unreleased]: https://git.allucanget.biz/zwitschi/gig-line/compare/v0.1.5...main
|
||||
[0.1.5]: https://git.allucanget.biz/zwitschi/gig-line/releases/tag/v0.1.5
|
||||
[0.1.4]: https://git.allucanget.biz/zwitschi/gig-line/releases/tag/v0.1.4
|
||||
[0.1.3]: https://git.allucanget.biz/zwitschi/gig-line/releases/tag/v0.1.3
|
||||
[0.1.2]: https://git.allucanget.biz/zwitschi/gig-line/releases/tag/v0.1.2
|
||||
[0.1.1]: https://git.allucanget.biz/zwitschi/gig-line/releases/tag/v0.1.1
|
||||
|
||||
@@ -20,11 +20,14 @@ Full requirements and progress are in [TODO.md](../../TODO.md); version directio
|
||||
|
||||
| Priority | Quality Goal | Scenario |
|
||||
| --- | --- | --- |
|
||||
| 1 | **Performance efficiency** | 60 FPS at 2560×1440 on RTX 5070 Ti, documented preset, no frame generation |
|
||||
| 2 | **Terrain walkability** | Zero raycast holes across both sidewalks and the carriageway (84-sample transect passes) |
|
||||
| 3 | **Geographic fidelity** | Building placement, street scale, and façade identity match reference photography |
|
||||
| 1 | **Visual realism** | Renders read as the reference photographs: façade identity, proportion, material and light — judged side by side, shot against photo |
|
||||
| 2 | **Geographic fidelity** | Building placement and street scale match the measured data and the photographs |
|
||||
| 3 | **Terrain walkability** | Zero raycast holes across both sidewalks and the carriageway (84-sample transect passes) |
|
||||
| 4 | **Build reproducibility** | Fresh checkout + documented engine path produces a runnable Linux build |
|
||||
| 5 | **Provenance** | Every committed asset has recorded provenance |
|
||||
| 6 | **Performance efficiency** | 60 FPS at 2560×1440 on RTX 5070 Ti, documented preset, no frame generation |
|
||||
|
||||
**How the order is used.** It decides trade-offs. When realism and performance disagree, realism wins and the cost is paid — but never by silently shipping a frame rate below the floor. The 60 FPS figure is a **budget that must still be met**, so the response to "realism costs too much frame time" is to cut the realism feature deliberately, record the cut, and keep the floor — not to let frames slip. Walkability outranks realism in one direction only: a beautiful block the player falls through is not a demo.
|
||||
|
||||
## Stakeholders
|
||||
|
||||
|
||||
@@ -30,7 +30,8 @@ All modelling and geographic processing live outside the game project in `Art/`,
|
||||
|
||||
## Quality achievement
|
||||
|
||||
- **Performance**: dynamic-only lighting (no build step, no static shadow maps), world-space-noise materials (cheap procedural variation), minimal overdraw (simple street-level geometry).
|
||||
- **Realism first**: reference photography and measured data drive every façade decision; renders are compared against the photographs with `Tools/visual_loop.py`, and a façade is built from the shared 4.96 m module kit so the street wall reads consistently. Materials are authored in Unreal with world-aligned generated textures, and lighting is fixed daytime so a shot is comparable between runs.
|
||||
- **Performance as a budget, not a driver**: dynamic-only lighting (no build step, no static shadow maps), world-space-noise materials (cheap procedural variation), minimal overdraw for street-level geometry, and a 60 FPS at 1440p floor that every release must meet. It closes the quality list because it constrains how much realism fits, not what the block should look like.
|
||||
|
||||
- **Walkability**: complex-as-simple collision on terrain (C++ helper forces the flag on the asset), block-all collision on every site mesh, swept transect QA verifies zero raycast holes.
|
||||
|
||||
|
||||
@@ -20,12 +20,14 @@ C++ classes in `Game/GigLine/Source/GigLine/`:
|
||||
| --- | --- | --- |
|
||||
| `AGigLineCharacter` | First-person player pawn | 1.75 m capsule, 4.5 m/s walk, Enhanced Input bindings, reset, pause, `-qaselftest` harness |
|
||||
| `AGigLineGameModeBase` | Sets game mode and HUD class | `HUDClass = AGigLineHUD` |
|
||||
| `AGigLineHUD` | HUD text overlay | `DrawText` every frame, bottom-left |
|
||||
| `AGigLineHUD` | HUD text overlay | `DrawText` every frame: ODbL attribution bottom-left, controls bottom-right, and an F3 debug readout top-left (`gigline.Debug`, on for `-perf`/`-qaselftest`) |
|
||||
| `AGigLineNPCPerson` | NPC prefab (future) | Capsule + visible MPFB body, feet on Z 0 |
|
||||
| `AGigLineShotActor` | Screenshot camera walker | Drives cameras in `-game` mode, `FScreenshotRequest`, quits |
|
||||
| `UGigLineAssetTools` | C++ collision helpers | `SetComplexCollisionAsSimple` on `StaticMesh` assets |
|
||||
|
||||
Input assets in `/Game/Input/`: `IA_Move`, `IA_MoveBack`, `IA_Strafe`, `IA_StrafeLeft`, `IA_Look`, `IA_Jump`, `IA_Reset`, `IA_Pause`, bound in `IMC_Default`.
|
||||
Input assets in `/Game/Input/`: `IA_Move`, `IA_MoveBack`, `IA_Strafe`, `IA_StrafeLeft`, `IA_Look`, `IA_Jump`, `IA_Reset`, `IA_Pause`, `IA_ToggleCamera`, `IA_ToggleDebug`, bound in `IMC_Default`.
|
||||
|
||||
Player-facing bindings: **WASD** move, **mouse** look, **Space** jump, **R** reset, **Escape** pause, **V** swap first/third person. The camera and body work landed with `0.2.0` (see ADR-9): the shipped default view stays first person, and `-thirdperson` starts the pawn in the third-person rig — used by the screenshot run so the review is reproducible.
|
||||
|
||||
### Site
|
||||
|
||||
@@ -47,6 +49,7 @@ Static meshes and materials in `/Game/Hollywood/Site/`:
|
||||
| `SM_HeroFacade` | El Capitan Entertainment Centre detailed façade |
|
||||
| `SM_ElCapitanTheatre`, `SM_Budget_6822`, `SM_Hollywood_6806`, `SM_Hollywood_6902`, `SM_Hollywood_6922`, `SM_LeeDrug_6800` | South block frontage (6 buildings) |
|
||||
| `SM_Madame_Tussauds`, `SM_TCL_Chinese_Theatre` | North-side buildings (within 50 m) |
|
||||
| `SM_Retail_LeeDrug`, `SM_Retail_6806` | Retail trim over 6800 and 6806–6820, modelled from their own photograph (`Tools/retail_frontage_build.py`, staged as `RetailTrim_*`). `6822 Budget` stays a bare volume: no photograph exists. |
|
||||
|
||||
### Art Pipeline
|
||||
|
||||
@@ -56,11 +59,15 @@ Python scripts in `Tools/` drive the content pipeline:
|
||||
| --- | --- |
|
||||
| `export_*`, `hero_facade_build` | Blender headless export (FBX) |
|
||||
| `ue_import_*`, `ue_hero_facade_import` | Unreal headless import + placement |
|
||||
| `character_anim_build`, `ue_character_anim_import` | Character animation clips (`GigLine_Idle`, `GigLine_Walk`) and their import |
|
||||
| `export_*`, `hero_facade_build`, `retail_frontage_build`, `theatre_trim_build`, `facade_kit_build` | Blender headless export (FBX); trim builders work in world coordinates |
|
||||
| `ue_import_*`, `ue_hero_facade_import`, `ue_import_retail_detail` | Unreal headless import + placement |
|
||||
| `ue_build_demo_map`, `ue_build_review_map` | Map assembly (lighting, meshes, player start) |
|
||||
| `ue_create_input_assets` | Enhanced Input actions + mapping context |
|
||||
| `render_screenshots.sh` | Screenshot capture pipeline |
|
||||
| `judge_screenshot.py` | Vision-model pre-check (optional) |
|
||||
| `input_emulation.py`, `input_test.py` | OS-level virtual keyboard/mouse injection for input verification |
|
||||
| `Tools/_common.py` | Pure helper shared by Blender builders, importable without Blender (bay layout) |
|
||||
| `Tools/tests/` | Stdlib `unittest` tests for the pure parts of the tools (`requirements-tools.txt`) |
|
||||
| `Geo/tools/mapbox_buildings.py` | Second height source: Mapbox `mapbox-streets-v8` building data vs our OSM heights |
|
||||
|
||||
|
||||
@@ -55,3 +55,19 @@
|
||||
- **Decision**: All Blender work driven by Python scripts in `Tools/`. Skill packs used as method guides (checklists, naming, proportions) only.
|
||||
|
||||
- **Consequences**: Scripts are version-controlled and repeatable. No live Blender session needed for CI.
|
||||
|
||||
## ADR-8: Realistic building façades are a minor milestone, not a patch
|
||||
|
||||
- **Context**: `0.1.4` carried the first façade pass as a _patch_, while the release policy says patches contain fixes and refinements and never new scope. The `0.1.6` retail pass then showed what that costs: a façade built in a patch has no release gate of its own, so a first pass that does not yet read as the building (`Screenshots/README.md`, 0.1.6 gaps) can sit inside a "done" patch series. Realism is also the project's leading quality goal, which a patch cannot carry.
|
||||
|
||||
- **Decision**: Building realism gets its own minor milestone, `0.9.0 — Realistic buildings`, placed before the feature freeze. Its scope is the façade system (orders, profile library, storefront types, signage as decals), per-building photo-matched models, a measured material library with a texel-density rule, and LODs plus collision proxies. Patches keep fixes and refinements only. Renumbered by this decision: the former `0.9.0` freeze/beta/audit milestone becomes `0.10.0`, its patches `0.10.1`–`0.10.3`, and `1.0.0` moves to weeks 39–44.
|
||||
|
||||
- **Consequences**: Façade work now has a release gate — every building measured against its own photograph, every building with a collision proxy and a LOD, no placeholder materials — so "modelled" means verified, not merely present. The price is that realism work stops being sliced into patches, and the schedule shifts by one minor cycle. Blockouts without a photograph stay blockouts and are listed as such, rather than being dressed up to fill the milestone.
|
||||
|
||||
## ADR-9: Third-person body is an attachment of the first-person pawn, not a second pawn
|
||||
|
||||
- **Context**: `0.2.0` needs a visible body and a third-person camera, while `0.1.0` shipped a first-person pawn with no body and the owner's decision was first person for that release. Two separate pawn classes would duplicate movement, input and reset logic, which is the code most likely to drift.
|
||||
|
||||
- **Decision**: one pawn, both rigs. `AGigLineCharacter` carries a skeletal mesh (hidden in first person), a collision-tested spring-arm camera, and a **V** toggle that swaps the active camera and the body's visibility. The shipped default view stays first person; `-thirdperson` starts the pawn in the third-person rig and is what the screenshot run passes, so the review shot is reproducible without a human pressing a key.
|
||||
|
||||
- **Consequences**: movement, reset, pause and the QA self-test stay in one place. The body's animation state is driven from `Tick` by speed rather than from input, so being pushed or falling animates correctly. The cost is that the camera swap is not a separate actor, so a future cinematic camera needs its own path rather than reusing the toggle.
|
||||
@@ -2,24 +2,44 @@
|
||||
|
||||
## Quality Requirements Overview
|
||||
|
||||
| Category | Requirement | Priority |
|
||||
Ordered by priority: realism first, performance last. The order decides design trade-offs; it does not license dropping a lower item silently (see the trade-off rule below the table).
|
||||
|
||||
| Priority | Category | Requirement |
|
||||
| --- | --- | --- |
|
||||
| Performance efficiency | 60 FPS at 2560×1440, documented preset, no frame generation | 1 |
|
||||
| Reliability / Terrain | Zero raycast holes across sidewalks and carriageway (84-sample transect) | 1 |
|
||||
| Accuracy / Fidelity | Building placement matches reference photography within ~1 m | 2 |
|
||||
| Accuracy / Fidelity | Street scale (lane width, sidewalk width) within ~10 cm of measured profile | 2 |
|
||||
| Usability | WASD + mouse look, jump, reset (R), pause (Escape) — no tutorial needed | 2 |
|
||||
| Maintainability | Fresh checkout builds with documented engine installation | 2 |
|
||||
| Portability | Linux build packages and runs; Windows build ships | 3 |
|
||||
| 1 | Realism / Fidelity | Façade identity, proportion, material and lighting read like the reference photographs when compared side by side |
|
||||
| 2 | Realism / Fidelity | Building placement matches the measured footprints and the photographs (within ~1 m) |
|
||||
| 2 | Realism / Fidelity | Street scale (lane width, sidewalk width) within ~10 cm of the measured profile |
|
||||
| 3 | Reliability / Terrain | Zero raycast holes across sidewalks and carriageway (84-sample transect) |
|
||||
| 3 | Usability | WASD + mouse look, jump, reset (R), pause (Escape) — no tutorial needed |
|
||||
| 4 | Maintainability | Fresh checkout builds with the documented engine installation |
|
||||
| 4 | Portability | Linux build packages and runs; Windows build ships |
|
||||
| 5 | Provenance | Every committed asset has a recorded source and licence |
|
||||
| 6 | Performance efficiency | 60 FPS at 2560×1440, documented preset, no frame generation |
|
||||
|
||||
**Trade-off rule.** Realism leads: when a realism improvement costs frame time, the improvement wins. Performance is still a **floor, not a preference** — 60 FPS at 1440p without frame generation must be met at every release. If realism cannot fit inside that budget, reduce the realism scope deliberately and record the reduction in `CHANGELOG.md`; never ship below the floor without the owner's explicit approval. Walkability outranks realism in one direction: a block the player falls through is not a demo, whatever it looks like.
|
||||
|
||||
## Quality Scenarios
|
||||
|
||||
| ID | Source/Stimulus | Response | Metric |
|
||||
| --- | --- | --- | --- |
|
||||
| QS-1 | Player walks both sides of the demo | No fall-through, character stays at ground Z | 0 raycast holes in 84-sample transect |
|
||||
| QS-2 | Player holds W for 2 s on flat road | Character moves forward | ≥ 200 cm displacement, Z constant within ±5 cm |
|
||||
| QS-3 | Player presses R | Character teleports to spawn | < 100 cm from `StartLocation` |
|
||||
| QS-4 | Player presses Escape | Game pauses, cursor appears | `IsGamePaused()` = true |
|
||||
| QS-5 | Player presses Escape while paused | Game unpauses, cursor hides | `IsGamePaused()` = false |
|
||||
| QS-6 | Game runs on dev workstation at 1440p | Frame rate stable | ≥ 60 FPS, 99th percentile ≥ 55 FPS |
|
||||
| QS-7 | Fresh checkout + engine install | Build succeeds | Exit code 0, runnable binary produced |
|
||||
| QS-1 | Screenshot is compared against its reference photograph | The render reads as the same building | Similarity score and visual match confirmed by the owner; `Tools/visual_loop.py` reports the deltas |
|
||||
| QS-2 | A façade is placed from measured data | It sits on its verified footprint | World span probed against `Geo/verification/` within ~1 m |
|
||||
| QS-3 | Player walks both sides of the demo | No fall-through, character stays at ground Z | 0 raycast holes in 84-sample transect |
|
||||
| QS-4 | Player holds W for 2 s on flat road | Character moves forward | ≥ 200 cm displacement, Z constant within ±5 cm |
|
||||
| QS-5 | Player presses R | Character teleports to spawn | < 100 cm from `StartLocation` |
|
||||
| QS-6 | Player presses Escape | Game pauses, cursor appears | `IsGamePaused()` = true |
|
||||
| QS-7 | Player presses Escape while paused | Game unpauses, cursor hides | `IsGamePaused()` = false |
|
||||
| QS-8 | Fresh checkout + engine install | Build succeeds | Exit code 0, runnable binary produced |
|
||||
| QS-9 | Game runs on dev workstation at 1440p | Frame rate holds the floor | ≥ 60 FPS, 99th percentile ≥ 55 FPS |
|
||||
|
||||
> QS-9 is last on purpose. Performance is a floor that every release must meet, not the goal that shapes the content: realism decides what gets built, the frame budget decides how much of it.
|
||||
|
||||
### Building realism gate (0.9.0)
|
||||
|
||||
| ID | Check | Metric |
|
||||
| --- | --- | --- |
|
||||
| BR-1 | A building with a reference photograph has a review shot compared against it | Silhouette and proportion deltas recorded per building in `Screenshots/README.md`; owner approval per shot |
|
||||
| BR-2 | Every building is either photo-matched or explicitly recorded as an unfounded blockout | List in `TODO.md`; no building in an in-between state |
|
||||
| BR-3 | Every building has a collision proxy and at least one LOD | Count of buildings missing either = 0 |
|
||||
| BR-4 | No surface carries a placeholder material | Material audit in `Tools/ue_audit_scene.py` reports 0 placeholder slots |
|
||||
| BR-5 | The realism pass does not break the floor | QS-9 still holds at 1440p, with the measured cost of the pass recorded |
|
||||
@@ -27,11 +27,14 @@ Full requirements and progress are in [TODO.md](../../TODO.md); version directio
|
||||
|
||||
| Priority | Quality Goal | Scenario |
|
||||
| --- | --- | --- |
|
||||
| 1 | **Performance efficiency** | 60 FPS at 2560×1440 on RTX 5070 Ti, documented preset, no frame generation |
|
||||
| 2 | **Terrain walkability** | Zero raycast holes across both sidewalks and the carriageway (84-sample transect passes) |
|
||||
| 3 | **Geographic fidelity** | Building placement, street scale, and façade identity match reference photography |
|
||||
| 1 | **Visual realism** | Renders read as the reference photographs: façade identity, proportion, material and light — judged side by side, shot against photo |
|
||||
| 2 | **Geographic fidelity** | Building placement and street scale match the measured data and the photographs |
|
||||
| 3 | **Terrain walkability** | Zero raycast holes across both sidewalks and the carriageway (84-sample transect passes) |
|
||||
| 4 | **Build reproducibility** | Fresh checkout + documented engine path produces a runnable Linux build |
|
||||
| 5 | **Provenance** | Every committed asset has recorded provenance |
|
||||
| 6 | **Performance efficiency** | 60 FPS at 2560×1440 on RTX 5070 Ti, documented preset, no frame generation |
|
||||
|
||||
**How the order is used.** It decides trade-offs. When realism and performance disagree, realism wins and the cost is paid — but never by silently shipping a frame rate below the floor. The 60 FPS figure is a **budget that must still be met**, so the response to "realism costs too much frame time" is to cut the realism feature deliberately, record the cut, and keep the floor — not to let frames slip. Walkability outranks realism in one direction only: a beautiful block the player falls through is not a demo.
|
||||
|
||||
## Stakeholders
|
||||
|
||||
@@ -129,7 +132,9 @@ All modelling and geographic processing live outside the game project in `Art/`,
|
||||
|
||||
## Quality achievement
|
||||
|
||||
- **Performance**: dynamic-only lighting (no build step, no static shadow maps), world-space-noise materials (cheap procedural variation), minimal overdraw (simple street-level geometry).
|
||||
- **Realism**: reference photography drives every façade decision, measured data fixes placement and scale, and renders are compared against the photographs (`Tools/visual_loop.py`, `Screenshots/README.md`). One module kit at the 4.96 m bay keeps the street wall coherent; materials are world-aligned and lit by a fixed daytime rig so a shot is comparable between runs.
|
||||
|
||||
- **Performance holds the floor, it does not set the agenda**: dynamic-only lighting (no build step, no static shadow maps), cheap procedural materials, minimal overdraw for street-level geometry, and 60 FPS at 1440p as a budget every release must meet.
|
||||
|
||||
- **Walkability**: complex-as-simple collision on terrain (C++ helper forces the flag on the asset), block-all collision on every site mesh, swept transect QA verifies zero raycast holes.
|
||||
|
||||
@@ -159,12 +164,14 @@ C++ classes in `Game/GigLine/Source/GigLine/`:
|
||||
| --- | --- | --- |
|
||||
| `AGigLineCharacter` | First-person player pawn | 1.75 m capsule, 4.5 m/s walk, Enhanced Input bindings, reset, pause, `-qaselftest` harness |
|
||||
| `AGigLineGameModeBase` | Sets game mode and HUD class | `HUDClass = AGigLineHUD` |
|
||||
| `AGigLineHUD` | HUD text overlay | `DrawText` every frame, bottom-left |
|
||||
| `AGigLineHUD` | HUD text overlay | `DrawText` every frame: ODbL attribution bottom-left, controls bottom-right, and an F3 debug readout top-left (`gigline.Debug`, on for `-perf`/`-qaselftest`) |
|
||||
| `AGigLineNPCPerson` | NPC prefab (future) | Capsule + visible MPFB body, feet on Z 0 |
|
||||
| `AGigLineShotActor` | Screenshot camera walker | Drives cameras in `-game` mode, `FScreenshotRequest`, quits |
|
||||
| `UGigLineAssetTools` | C++ collision helpers | `SetComplexCollisionAsSimple` on `StaticMesh` assets |
|
||||
|
||||
Input assets in `/Game/Input/`: `IA_Move`, `IA_MoveBack`, `IA_Strafe`, `IA_StrafeLeft`, `IA_Look`, `IA_Jump`, `IA_Reset`, `IA_Pause`, bound in `IMC_Default`.
|
||||
Input assets in `/Game/Input/`: `IA_Move`, `IA_MoveBack`, `IA_Strafe`, `IA_StrafeLeft`, `IA_Look`, `IA_Jump`, `IA_Reset`, `IA_Pause`, `IA_ToggleCamera`, `IA_ToggleDebug`, bound in `IMC_Default`.
|
||||
|
||||
Player-facing bindings: **WASD** move, **mouse** look, **Space** jump, **R** reset, **Escape** pause, **V** swap first/third person. The camera and body work landed with `0.2.0` (see ADR-9): the shipped default view stays first person, and `-thirdperson` starts the pawn in the third-person rig — used by the screenshot run so the review is reproducible.
|
||||
|
||||
### Site
|
||||
|
||||
@@ -186,6 +193,7 @@ Static meshes and materials in `/Game/Hollywood/Site/`:
|
||||
| `SM_HeroFacade` | El Capitan Entertainment Centre detailed façade |
|
||||
| `SM_ElCapitanTheatre`, `SM_Budget_6822`, `SM_Hollywood_6806`, `SM_Hollywood_6902`, `SM_Hollywood_6922`, `SM_LeeDrug_6800` | South block frontage (6 buildings) |
|
||||
| `SM_Madame_Tussauds`, `SM_TCL_Chinese_Theatre` | North-side buildings (within 50 m) |
|
||||
| `SM_Retail_LeeDrug`, `SM_Retail_6806` | Retail trim over 6800 and 6806–6820, modelled from their own photograph (`Tools/retail_frontage_build.py`, staged as `RetailTrim_*`). `6822 Budget` stays a bare volume: no photograph exists. |
|
||||
|
||||
### Art Pipeline
|
||||
|
||||
@@ -195,11 +203,15 @@ Python scripts in `Tools/` drive the content pipeline:
|
||||
| --- | --- |
|
||||
| `export_*`, `hero_facade_build` | Blender headless export (FBX) |
|
||||
| `ue_import_*`, `ue_hero_facade_import` | Unreal headless import + placement |
|
||||
| `character_anim_build`, `ue_character_anim_import` | Character animation clips (`GigLine_Idle`, `GigLine_Walk`) and their import |
|
||||
| `export_*`, `hero_facade_build`, `retail_frontage_build`, `theatre_trim_build`, `facade_kit_build` | Blender headless export (FBX); trim builders work in world coordinates |
|
||||
| `ue_import_*`, `ue_hero_facade_import`, `ue_import_retail_detail` | Unreal headless import + placement |
|
||||
| `ue_build_demo_map`, `ue_build_review_map` | Map assembly (lighting, meshes, player start) |
|
||||
| `ue_create_input_assets` | Enhanced Input actions + mapping context |
|
||||
| `render_screenshots.sh` | Screenshot capture pipeline |
|
||||
| `judge_screenshot.py` | Vision-model pre-check (optional) |
|
||||
| `input_emulation.py`, `input_test.py` | OS-level virtual keyboard/mouse injection for input verification |
|
||||
| `Tools/_common.py` | Pure helper shared by Blender builders, importable without Blender (bay layout) |
|
||||
| `Tools/tests/` | Stdlib `unittest` tests for the pure parts of the tools (`requirements-tools.txt`) |
|
||||
| `Geo/tools/mapbox_buildings.py` | Second height source: Mapbox `mapbox-streets-v8` building data vs our OSM heights |
|
||||
|
||||
@@ -446,31 +458,67 @@ Rule: place a building only against a neighbour or street mesh, never in isolati
|
||||
|
||||
- **Consequences**: Scripts are version-controlled and repeatable. No live Blender session needed for CI.
|
||||
|
||||
## ADR-8: Realistic building façades are a minor milestone, not a patch
|
||||
|
||||
- **Context**: `0.1.4` carried the first façade pass as a _patch_, while the release policy says patches contain fixes and refinements and never new scope. The `0.1.6` retail pass then showed what that costs: a façade built in a patch has no release gate of its own, so a first pass that does not yet read as the building (`Screenshots/README.md`, 0.1.6 gaps) can sit inside a "done" patch series. Realism is also the project's leading quality goal, which a patch cannot carry.
|
||||
|
||||
- **Decision**: Building realism gets its own minor milestone, `0.9.0 — Realistic buildings`, placed before the feature freeze. Its scope is the façade system (orders, profile library, storefront types, signage as decals), per-building photo-matched models, a measured material library with a texel-density rule, and LODs plus collision proxies. Patches keep fixes and refinements only. Renumbered by this decision: the former `0.9.0` freeze/beta/audit milestone becomes `0.10.0`, its patches `0.10.1`–`0.10.3`, and `1.0.0` moves to weeks 39–44.
|
||||
|
||||
- **Consequences**: Façade work now has a release gate — every building measured against its own photograph, every building with a collision proxy and a LOD, no placeholder materials — so "modelled" means verified, not merely present. The price is that realism work stops being sliced into patches, and the schedule shifts by one minor cycle. Blockouts without a photograph stay blockouts and are listed as such, rather than being dressed up to fill the milestone.
|
||||
|
||||
## ADR-9: Third-person body is an attachment of the first-person pawn, not a second pawn
|
||||
|
||||
- **Context**: `0.2.0` needs a visible body and a third-person camera, while `0.1.0` shipped a first-person pawn with no body and the owner's decision was first person for that release. Two separate pawn classes would duplicate movement, input and reset logic, which is the code most likely to drift.
|
||||
|
||||
- **Decision**: one pawn, both rigs. `AGigLineCharacter` carries a skeletal mesh (hidden in first person), a collision-tested spring-arm camera, and a **V** toggle that swaps the active camera and the body's visibility. The shipped default view stays first person; `-thirdperson` starts the pawn in the third-person rig and is what the screenshot run passes, so the review shot is reproducible without a human pressing a key.
|
||||
|
||||
- **Consequences**: movement, reset, pause and the QA self-test stay in one place. The body's animation state is driven from `Tick` by speed rather than from input, so being pushed or falling animates correctly. The cost is that the camera swap is not a separate actor, so a future cinematic camera needs its own path rather than reusing the toggle.
|
||||
|
||||
# Quality Requirements
|
||||
|
||||
## Quality Requirements Overview
|
||||
|
||||
| Category | Requirement | Priority |
|
||||
Ordered by priority: realism first, performance last. The order decides design trade-offs; it does not license dropping a lower item silently (see the trade-off rule below the table).
|
||||
|
||||
| Priority | Category | Requirement |
|
||||
| --- | --- | --- |
|
||||
| Performance efficiency | 60 FPS at 2560×1440, documented preset, no frame generation | 1 |
|
||||
| Reliability / Terrain | Zero raycast holes across sidewalks and carriageway (84-sample transect) | 1 |
|
||||
| Accuracy / Fidelity | Building placement matches reference photography within ~1 m | 2 |
|
||||
| Accuracy / Fidelity | Street scale (lane width, sidewalk width) within ~10 cm of measured profile | 2 |
|
||||
| Usability | WASD + mouse look, jump, reset (R), pause (Escape) — no tutorial needed | 2 |
|
||||
| Maintainability | Fresh checkout builds with documented engine installation | 2 |
|
||||
| Portability | Linux build packages and runs; Windows build ships | 3 |
|
||||
| 1 | Realism / Fidelity | Façade identity, proportion, material and lighting read like the reference photographs when compared side by side |
|
||||
| 2 | Realism / Fidelity | Building placement matches the measured footprints and the photographs (within ~1 m) |
|
||||
| 2 | Realism / Fidelity | Street scale (lane width, sidewalk width) within ~10 cm of the measured profile |
|
||||
| 3 | Reliability / Terrain | Zero raycast holes across sidewalks and carriageway (84-sample transect) |
|
||||
| 3 | Usability | WASD + mouse look, jump, reset (R), pause (Escape) — no tutorial needed |
|
||||
| 4 | Maintainability | Fresh checkout builds with the documented engine installation |
|
||||
| 4 | Portability | Linux build packages and runs; Windows build ships |
|
||||
| 5 | Provenance | Every committed asset has a recorded source and licence |
|
||||
| 6 | Performance efficiency | 60 FPS at 2560×1440, documented preset, no frame generation |
|
||||
|
||||
**Trade-off rule.** Realism leads: when a realism improvement costs frame time, the improvement wins. Performance is still a **floor, not a preference** — 60 FPS at 1440p without frame generation must be met at every release. If realism cannot fit inside that budget, reduce the realism scope deliberately and record the reduction in `CHANGELOG.md`; never ship below the floor without the owner's explicit approval. Walkability outranks realism in one direction: a block the player falls through is not a demo, whatever it looks like.
|
||||
|
||||
## Quality Scenarios
|
||||
|
||||
| ID | Source/Stimulus | Response | Metric |
|
||||
| --- | --- | --- | --- |
|
||||
| QS-1 | Player walks both sides of the demo | No fall-through, character stays at ground Z | 0 raycast holes in 84-sample transect |
|
||||
| QS-2 | Player holds W for 2 s on flat road | Character moves forward | ≥ 200 cm displacement, Z constant within ±5 cm |
|
||||
| QS-3 | Player presses R | Character teleports to spawn | < 100 cm from `StartLocation` |
|
||||
| QS-4 | Player presses Escape | Game pauses, cursor appears | `IsGamePaused()` = true |
|
||||
| QS-5 | Player presses Escape while paused | Game unpauses, cursor hides | `IsGamePaused()` = false |
|
||||
| QS-6 | Game runs on dev workstation at 1440p | Frame rate stable | ≥ 60 FPS, 99th percentile ≥ 55 FPS |
|
||||
| QS-7 | Fresh checkout + engine install | Build succeeds | Exit code 0, runnable binary produced |
|
||||
| QS-1 | Screenshot is compared against its reference photograph | The render reads as the same building | Similarity score and visual match confirmed by the owner; `Tools/visual_loop.py` reports the deltas |
|
||||
| QS-2 | A façade is placed from measured data | It sits on its verified footprint | World span probed against `Geo/verification/` within ~1 m |
|
||||
| QS-3 | Player walks both sides of the demo | No fall-through, character stays at ground Z | 0 raycast holes in 84-sample transect |
|
||||
| QS-4 | Player holds W for 2 s on flat road | Character moves forward | ≥ 200 cm displacement, Z constant within ±5 cm |
|
||||
| QS-5 | Player presses R | Character teleports to spawn | < 100 cm from `StartLocation` |
|
||||
| QS-6 | Player presses Escape | Game pauses, cursor appears | `IsGamePaused()` = true |
|
||||
| QS-7 | Player presses Escape while paused | Game unpauses, cursor hides | `IsGamePaused()` = false |
|
||||
| QS-8 | Fresh checkout + engine install | Build succeeds | Exit code 0, runnable binary produced |
|
||||
| QS-9 | Game runs on dev workstation at 1440p | Frame rate holds the floor | ≥ 60 FPS, 99th percentile ≥ 55 FPS |
|
||||
|
||||
> QS-9 is last on purpose. Performance is a floor that every release must meet, not the goal that shapes the content: realism decides what gets built, the frame budget decides how much of it.
|
||||
|
||||
### Building realism gate (0.9.0)
|
||||
|
||||
| ID | Check | Metric |
|
||||
| --- | --- | --- |
|
||||
| BR-1 | A building with a reference photograph has a review shot compared against it | Silhouette and proportion deltas recorded per building in `Screenshots/README.md`; owner approval per shot |
|
||||
| BR-2 | Every building is either photo-matched or explicitly recorded as an unfounded blockout | List in `TODO.md`; no building in an in-between state |
|
||||
| BR-3 | Every building has a collision proxy and at least one LOD | Count of buildings missing either = 0 |
|
||||
| BR-4 | No surface carries a placeholder material | Material audit in `Tools/ue_audit_scene.py` reports 0 placeholder slots |
|
||||
| BR-5 | The realism pass does not break the floor | QS-9 still holds at 1440p, with the measured cost of the pass recorded |
|
||||
|
||||
# Risks and Technical Debts
|
||||
|
||||
|
||||
@@ -86,8 +86,13 @@ The hero block and the demo level are built by a fixed sequence. Each step write
|
||||
| North import | `Tools/ue_import_north_buildings.py` | `/Game/Hollywood/North/*` |
|
||||
| Hero import | `Tools/ue_hero_facade_import.py` | `/Game/Hollywood/ElCapitan/SM_HeroFacade` |
|
||||
| Demo map | `Tools/ue_build_demo_map.py` | `/Game/Hollywood/DemoMap` |
|
||||
| Façade kit assembly | `Tools/ue_apply_facade_kit.py` | kit actors in `DemoMap` |
|
||||
| Theatre trim | `Tools/ue_stage_theatre_trim.py` | marquee + blade sign actors |
|
||||
| Scene audit | `Tools/ue_audit_scene.py` | materials, placement spans, labels (gate) |
|
||||
| Review shots | `Tools/ue_build_review_map.py` + `Tools/render_screenshots.sh` | `Screenshots/` |
|
||||
|
||||
**Order matters.** `ue_build_demo_map.py` rebuilds the site materials, so any actor staged by another tool must be (re)staged _after_ it, or it keeps a reference to a material that no longer exists — that is how 604 actors once ended up untethered from their materials while the map only looked slightly flat. `Tools/render_screenshots.sh` runs the full chain in the right order and gates on `ue_audit_scene.py`.
|
||||
|
||||
Every Blender exporter calls [`Tools/fbx_export_preset.py`](../Tools/fbx_export_preset.py) (`ue_metric_v1` / `ue_character_v1`) instead of repeating the FBX argument block — one place for the scale conversion, the axis mapping and `mesh_smooth_type="FACE"` (without which every Unreal import warns about missing smoothing groups and round geometry arrives faceted).
|
||||
|
||||
Two rules that the pipeline enforces rather than documents:
|
||||
|
||||
+28
-1
@@ -1,4 +1,4 @@
|
||||
# Controls (`0.1.0` walkable demo)
|
||||
# Controls
|
||||
|
||||
Keyboard and mouse. The same list is shown in-game, bottom-right of the frame (`AGigLineHUD`), and the packaged Linux build ships with these bindings.
|
||||
|
||||
@@ -12,6 +12,8 @@ Keyboard and mouse. The same list is shown in-game, bottom-right of the frame (`
|
||||
| Jump | `Space` | |
|
||||
| Reset to start | `R` | Returns to the grounded spawn point |
|
||||
| Pause / release mouse | `Escape` | Second press resumes and recaptures the mouse |
|
||||
| Swap first / third person | `V` | First person is the default; the body is hidden in that view |
|
||||
| Show / hide debug overlay | `F3` | FPS, frame time, worst frame, view mode, speed and player position |
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -19,6 +21,31 @@ Keyboard and mouse. The same list is shown in-game, bottom-right of the frame (`
|
||||
- The mouse is captured on launch, so looking works without clicking first. `Escape` toggles the capture as part of pause.
|
||||
- Bindings are Enhanced Input actions in `/Game/Input` built by `Tools/ue_create_input_assets.py`; the mapping context is `IMC_Default`.
|
||||
|
||||
## Changes since `0.1.0`
|
||||
|
||||
- **`V` swaps first and third person** (`0.2.0`, `IA_ToggleCamera`). The shipped default view is still first person, as the owner decided for `0.1.0`; third person adds a collision-tested spring-arm camera and a visible, animated body. `Escape` still pauses and releases the mouse in either view.
|
||||
|
||||
## Debug overlay
|
||||
|
||||
`F3` toggles a top-left readout: smoothed **FPS** and frame time, the **worst** frame in a rolling two-second window, the active view (first or third person), player speed, a frame counter, and the player's position in metres from the project origin. The first two seconds of a session are a warm-up window and are excluded from the worst-frame figure, because shader and streaming hitches there made a healthy run read as a 400 ms stall.
|
||||
|
||||
It is **off in the built demo** — a player should not see an FPS counter over Hollywood Boulevard — and **on automatically** when the process is started with `-perf` or `-qaselftest`, so a performance capture never depends on someone pressing a key first. In a running build it can also be forced with the console variable `gigline.Debug 1`.
|
||||
|
||||
Cost: about **5 %** of frame time at 1440p in the demo (2.64 → 2.77 ms average), measured in the same self-test route. The performance release check is recorded without the overlay for that reason; the overlay run is kept beside it as `Docs/perf/0.2.0-walk-frametimes-overlay.csv`.
|
||||
|
||||
## Review and harness switches
|
||||
|
||||
These are command-line switches for the screenshot pipeline and QA runs, not player controls. They exist so a capture is reproducible rather than dependent on a human pressing keys at the right moment.
|
||||
|
||||
| Switch | Effect |
|
||||
| --- | --- |
|
||||
| `-thirdperson` | Starts the pawn in the third-person rig, so a capture shows the body without anyone pressing `V` |
|
||||
| `-walk` | Holds forward input from the first frame — what makes the stride shot possible |
|
||||
| `-qaselftest` | Runs the scripted walk / reset / pause / transect checks and quits; also enables the debug overlay |
|
||||
| `-perf` | Enables the debug overlay for a manual frame-time check |
|
||||
|
||||
The walking shot is driven per shot rather than by the switch: the shot actor enables the walk only while a tracking (`ShotFollow`) camera is being captured, and returns the pawn to spawn afterwards, so the fixed-camera shots still find it in place.
|
||||
|
||||
## Testing input without a physical keyboard
|
||||
|
||||
`Tools/input_test.py` injects OS-level keyboard/mouse events through a virtual `/dev/uinput` device (see `Tools/input_emulation.py`), e.g.:
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,728 @@
|
||||
frame,ms
|
||||
0,2.599
|
||||
1,2.877
|
||||
2,2.709
|
||||
3,2.646
|
||||
4,3.168
|
||||
5,2.729
|
||||
6,2.524
|
||||
7,2.726
|
||||
8,2.639
|
||||
9,2.814
|
||||
10,2.819
|
||||
11,2.708
|
||||
12,2.822
|
||||
13,2.703
|
||||
14,2.365
|
||||
15,2.796
|
||||
16,2.899
|
||||
17,2.972
|
||||
18,2.908
|
||||
19,2.356
|
||||
20,2.781
|
||||
21,2.574
|
||||
22,3.148
|
||||
23,2.306
|
||||
24,2.668
|
||||
25,2.855
|
||||
26,2.588
|
||||
27,3.321
|
||||
28,3.419
|
||||
29,3.121
|
||||
30,3.391
|
||||
31,2.355
|
||||
32,2.679
|
||||
33,3.463
|
||||
34,2.614
|
||||
35,2.922
|
||||
36,2.429
|
||||
37,2.830
|
||||
38,2.842
|
||||
39,2.782
|
||||
40,3.252
|
||||
41,2.731
|
||||
42,2.577
|
||||
43,2.363
|
||||
44,2.786
|
||||
45,2.547
|
||||
46,2.594
|
||||
47,2.652
|
||||
48,2.836
|
||||
49,2.792
|
||||
50,2.552
|
||||
51,2.792
|
||||
52,2.718
|
||||
53,3.333
|
||||
54,2.165
|
||||
55,2.880
|
||||
56,2.613
|
||||
57,2.460
|
||||
58,2.919
|
||||
59,2.770
|
||||
60,2.551
|
||||
61,2.622
|
||||
62,2.712
|
||||
63,2.818
|
||||
64,3.155
|
||||
65,2.472
|
||||
66,2.861
|
||||
67,2.651
|
||||
68,2.573
|
||||
69,2.892
|
||||
70,2.689
|
||||
71,2.729
|
||||
72,2.668
|
||||
73,2.753
|
||||
74,2.812
|
||||
75,2.606
|
||||
76,2.686
|
||||
77,2.842
|
||||
78,2.660
|
||||
79,2.903
|
||||
80,2.486
|
||||
81,2.591
|
||||
82,3.626
|
||||
83,2.455
|
||||
84,2.475
|
||||
85,2.922
|
||||
86,2.603
|
||||
87,2.728
|
||||
88,3.057
|
||||
89,2.731
|
||||
90,3.632
|
||||
91,1.950
|
||||
92,2.694
|
||||
93,3.020
|
||||
94,2.230
|
||||
95,2.796
|
||||
96,2.707
|
||||
97,2.807
|
||||
98,2.658
|
||||
99,2.697
|
||||
100,2.453
|
||||
101,3.051
|
||||
102,2.758
|
||||
103,2.662
|
||||
104,2.599
|
||||
105,2.733
|
||||
106,2.751
|
||||
107,2.894
|
||||
108,2.501
|
||||
109,2.753
|
||||
110,2.873
|
||||
111,2.439
|
||||
112,2.634
|
||||
113,2.682
|
||||
114,2.913
|
||||
115,2.692
|
||||
116,2.735
|
||||
117,2.758
|
||||
118,2.726
|
||||
119,2.907
|
||||
120,2.568
|
||||
121,2.764
|
||||
122,2.815
|
||||
123,2.530
|
||||
124,2.684
|
||||
125,2.719
|
||||
126,2.846
|
||||
127,2.879
|
||||
128,2.647
|
||||
129,2.770
|
||||
130,2.477
|
||||
131,2.657
|
||||
132,2.675
|
||||
133,2.917
|
||||
134,2.700
|
||||
135,2.493
|
||||
136,2.678
|
||||
137,2.786
|
||||
138,3.004
|
||||
139,2.697
|
||||
140,2.642
|
||||
141,2.624
|
||||
142,2.684
|
||||
143,2.717
|
||||
144,2.743
|
||||
145,2.740
|
||||
146,2.825
|
||||
147,2.849
|
||||
148,2.991
|
||||
149,2.297
|
||||
150,3.084
|
||||
151,2.459
|
||||
152,3.214
|
||||
153,2.260
|
||||
154,2.550
|
||||
155,2.621
|
||||
156,2.660
|
||||
157,2.739
|
||||
158,2.574
|
||||
159,3.078
|
||||
160,2.735
|
||||
161,2.607
|
||||
162,2.793
|
||||
163,2.798
|
||||
164,2.584
|
||||
165,2.637
|
||||
166,2.627
|
||||
167,2.726
|
||||
168,2.656
|
||||
169,2.788
|
||||
170,2.674
|
||||
171,2.782
|
||||
172,2.766
|
||||
173,2.613
|
||||
174,2.724
|
||||
175,2.770
|
||||
176,2.709
|
||||
177,2.700
|
||||
178,2.600
|
||||
179,2.686
|
||||
180,2.747
|
||||
181,2.841
|
||||
182,2.601
|
||||
183,2.666
|
||||
184,2.846
|
||||
185,2.599
|
||||
186,2.806
|
||||
187,3.126
|
||||
188,2.521
|
||||
189,2.634
|
||||
190,2.716
|
||||
191,2.584
|
||||
192,2.714
|
||||
193,2.923
|
||||
194,2.277
|
||||
195,2.706
|
||||
196,2.606
|
||||
197,2.758
|
||||
198,2.747
|
||||
199,2.958
|
||||
200,2.798
|
||||
201,2.387
|
||||
202,2.596
|
||||
203,2.861
|
||||
204,2.801
|
||||
205,2.521
|
||||
206,2.869
|
||||
207,2.884
|
||||
208,2.515
|
||||
209,2.592
|
||||
210,3.723
|
||||
211,2.474
|
||||
212,2.736
|
||||
213,2.774
|
||||
214,2.769
|
||||
215,3.007
|
||||
216,2.768
|
||||
217,2.716
|
||||
218,2.694
|
||||
219,2.722
|
||||
220,2.862
|
||||
221,2.587
|
||||
222,2.833
|
||||
223,2.792
|
||||
224,2.746
|
||||
225,2.585
|
||||
226,2.741
|
||||
227,2.585
|
||||
228,2.802
|
||||
229,2.674
|
||||
230,2.742
|
||||
231,2.718
|
||||
232,2.881
|
||||
233,2.707
|
||||
234,2.585
|
||||
235,2.810
|
||||
236,2.948
|
||||
237,2.718
|
||||
238,2.773
|
||||
239,2.571
|
||||
240,2.788
|
||||
241,2.702
|
||||
242,2.634
|
||||
243,2.788
|
||||
244,2.678
|
||||
245,2.914
|
||||
246,2.527
|
||||
247,2.637
|
||||
248,2.877
|
||||
249,2.693
|
||||
250,2.644
|
||||
251,2.670
|
||||
252,2.700
|
||||
253,2.877
|
||||
254,2.638
|
||||
255,2.617
|
||||
256,2.774
|
||||
257,2.660
|
||||
258,2.731
|
||||
259,2.671
|
||||
260,2.838
|
||||
261,2.726
|
||||
262,2.671
|
||||
263,2.744
|
||||
264,2.735
|
||||
265,2.684
|
||||
266,3.850
|
||||
267,2.120
|
||||
268,2.669
|
||||
269,2.729
|
||||
270,2.712
|
||||
271,2.817
|
||||
272,2.926
|
||||
273,2.678
|
||||
274,2.786
|
||||
275,2.927
|
||||
276,2.493
|
||||
277,2.846
|
||||
278,2.610
|
||||
279,2.644
|
||||
280,2.577
|
||||
281,2.939
|
||||
282,2.727
|
||||
283,2.595
|
||||
284,2.819
|
||||
285,2.778
|
||||
286,2.774
|
||||
287,2.628
|
||||
288,2.711
|
||||
289,2.699
|
||||
290,2.715
|
||||
291,2.763
|
||||
292,2.710
|
||||
293,2.892
|
||||
294,2.721
|
||||
295,2.575
|
||||
296,3.608
|
||||
297,2.811
|
||||
298,2.739
|
||||
299,2.751
|
||||
300,2.608
|
||||
301,2.913
|
||||
302,3.052
|
||||
303,2.862
|
||||
304,2.653
|
||||
305,2.727
|
||||
306,2.646
|
||||
307,2.888
|
||||
308,3.338
|
||||
309,2.743
|
||||
310,2.639
|
||||
311,2.681
|
||||
312,2.846
|
||||
313,2.766
|
||||
314,3.184
|
||||
315,2.834
|
||||
316,2.728
|
||||
317,2.581
|
||||
318,2.770
|
||||
319,3.351
|
||||
320,3.157
|
||||
321,2.508
|
||||
322,2.835
|
||||
323,2.754
|
||||
324,2.411
|
||||
325,3.398
|
||||
326,2.802
|
||||
327,2.611
|
||||
328,3.058
|
||||
329,2.407
|
||||
330,2.640
|
||||
331,3.645
|
||||
332,2.729
|
||||
333,2.580
|
||||
334,2.902
|
||||
335,2.523
|
||||
336,3.005
|
||||
337,3.173
|
||||
338,2.840
|
||||
339,2.610
|
||||
340,2.576
|
||||
341,2.706
|
||||
342,3.405
|
||||
343,2.849
|
||||
344,2.678
|
||||
345,2.796
|
||||
346,2.782
|
||||
347,2.636
|
||||
348,3.475
|
||||
349,2.798
|
||||
350,2.745
|
||||
351,2.564
|
||||
352,2.792
|
||||
353,2.714
|
||||
354,3.496
|
||||
355,2.841
|
||||
356,2.465
|
||||
357,2.708
|
||||
358,2.692
|
||||
359,2.934
|
||||
360,3.304
|
||||
361,2.561
|
||||
362,2.689
|
||||
363,2.749
|
||||
364,2.780
|
||||
365,2.940
|
||||
366,3.025
|
||||
367,2.706
|
||||
368,2.881
|
||||
369,2.619
|
||||
370,2.716
|
||||
371,3.133
|
||||
372,2.874
|
||||
373,2.816
|
||||
374,2.414
|
||||
375,2.737
|
||||
376,2.797
|
||||
377,2.651
|
||||
378,2.920
|
||||
379,3.032
|
||||
380,2.606
|
||||
381,2.710
|
||||
382,2.649
|
||||
383,2.700
|
||||
384,3.469
|
||||
385,2.224
|
||||
386,2.567
|
||||
387,2.733
|
||||
388,2.511
|
||||
389,2.950
|
||||
390,3.979
|
||||
391,2.798
|
||||
392,2.487
|
||||
393,2.740
|
||||
394,2.741
|
||||
395,2.718
|
||||
396,3.291
|
||||
397,2.769
|
||||
398,2.697
|
||||
399,2.796
|
||||
400,2.641
|
||||
401,2.652
|
||||
402,3.151
|
||||
403,2.673
|
||||
404,2.741
|
||||
405,2.606
|
||||
406,2.665
|
||||
407,2.859
|
||||
408,2.739
|
||||
409,2.586
|
||||
410,2.910
|
||||
411,2.745
|
||||
412,2.698
|
||||
413,2.670
|
||||
414,2.797
|
||||
415,2.829
|
||||
416,2.643
|
||||
417,2.776
|
||||
418,2.772
|
||||
419,2.530
|
||||
420,2.567
|
||||
421,2.826
|
||||
422,2.782
|
||||
423,2.738
|
||||
424,2.640
|
||||
425,2.967
|
||||
426,2.600
|
||||
427,3.020
|
||||
428,2.502
|
||||
429,2.642
|
||||
430,2.625
|
||||
431,2.770
|
||||
432,2.659
|
||||
433,2.717
|
||||
434,2.698
|
||||
435,2.780
|
||||
436,2.992
|
||||
437,2.686
|
||||
438,2.560
|
||||
439,3.048
|
||||
440,2.721
|
||||
441,2.507
|
||||
442,2.694
|
||||
443,2.695
|
||||
444,2.892
|
||||
445,3.443
|
||||
446,2.633
|
||||
447,2.908
|
||||
448,2.823
|
||||
449,2.346
|
||||
450,2.599
|
||||
451,2.914
|
||||
452,2.774
|
||||
453,3.391
|
||||
454,2.384
|
||||
455,2.585
|
||||
456,2.667
|
||||
457,2.704
|
||||
458,2.674
|
||||
459,2.755
|
||||
460,2.718
|
||||
461,2.804
|
||||
462,2.583
|
||||
463,2.694
|
||||
464,2.797
|
||||
465,3.079
|
||||
466,2.434
|
||||
467,2.723
|
||||
468,3.671
|
||||
469,2.211
|
||||
470,2.567
|
||||
471,2.701
|
||||
472,2.717
|
||||
473,2.657
|
||||
474,2.581
|
||||
475,2.889
|
||||
476,3.006
|
||||
477,2.595
|
||||
478,2.703
|
||||
479,2.498
|
||||
480,2.694
|
||||
481,2.915
|
||||
482,2.655
|
||||
483,2.686
|
||||
484,2.833
|
||||
485,2.717
|
||||
486,2.723
|
||||
487,2.451
|
||||
488,2.913
|
||||
489,2.784
|
||||
490,2.684
|
||||
491,2.651
|
||||
492,2.809
|
||||
493,2.819
|
||||
494,2.676
|
||||
495,2.851
|
||||
496,2.775
|
||||
497,2.638
|
||||
498,2.520
|
||||
499,2.824
|
||||
500,2.930
|
||||
501,2.757
|
||||
502,2.608
|
||||
503,2.808
|
||||
504,2.508
|
||||
505,2.819
|
||||
506,2.654
|
||||
507,2.748
|
||||
508,2.705
|
||||
509,2.798
|
||||
510,2.559
|
||||
511,2.694
|
||||
512,2.821
|
||||
513,2.706
|
||||
514,2.612
|
||||
515,2.928
|
||||
516,2.475
|
||||
517,2.830
|
||||
518,2.658
|
||||
519,2.784
|
||||
520,2.572
|
||||
521,2.935
|
||||
522,2.688
|
||||
523,2.600
|
||||
524,2.675
|
||||
525,2.828
|
||||
526,2.774
|
||||
527,2.848
|
||||
528,2.764
|
||||
529,2.379
|
||||
530,2.962
|
||||
531,2.989
|
||||
532,2.528
|
||||
533,2.639
|
||||
534,2.906
|
||||
535,2.543
|
||||
536,2.582
|
||||
537,2.949
|
||||
538,2.813
|
||||
539,2.504
|
||||
540,2.754
|
||||
541,2.638
|
||||
542,2.695
|
||||
543,2.662
|
||||
544,2.922
|
||||
545,2.616
|
||||
546,2.810
|
||||
547,2.675
|
||||
548,2.551
|
||||
549,2.894
|
||||
550,2.754
|
||||
551,2.632
|
||||
552,2.748
|
||||
553,2.827
|
||||
554,2.751
|
||||
555,2.759
|
||||
556,2.684
|
||||
557,2.766
|
||||
558,2.948
|
||||
559,2.461
|
||||
560,2.621
|
||||
561,2.772
|
||||
562,2.985
|
||||
563,2.582
|
||||
564,2.897
|
||||
565,2.574
|
||||
566,2.661
|
||||
567,2.742
|
||||
568,2.797
|
||||
569,2.632
|
||||
570,2.755
|
||||
571,2.878
|
||||
572,2.759
|
||||
573,3.812
|
||||
574,2.651
|
||||
575,2.862
|
||||
576,2.577
|
||||
577,2.634
|
||||
578,3.173
|
||||
579,2.418
|
||||
580,2.714
|
||||
581,2.746
|
||||
582,2.794
|
||||
583,2.774
|
||||
584,2.583
|
||||
585,3.659
|
||||
586,3.222
|
||||
587,2.489
|
||||
588,2.868
|
||||
589,2.558
|
||||
590,2.586
|
||||
591,2.599
|
||||
592,2.930
|
||||
593,2.401
|
||||
594,2.540
|
||||
595,2.889
|
||||
596,2.733
|
||||
597,2.664
|
||||
598,2.877
|
||||
599,2.588
|
||||
600,2.677
|
||||
601,2.897
|
||||
602,2.672
|
||||
603,2.780
|
||||
604,2.740
|
||||
605,2.780
|
||||
606,2.493
|
||||
607,2.794
|
||||
608,2.631
|
||||
609,2.768
|
||||
610,2.870
|
||||
611,2.626
|
||||
612,2.862
|
||||
613,2.602
|
||||
614,2.868
|
||||
615,2.627
|
||||
616,2.638
|
||||
617,2.702
|
||||
618,2.904
|
||||
619,2.918
|
||||
620,2.487
|
||||
621,3.225
|
||||
622,2.350
|
||||
623,2.820
|
||||
624,2.837
|
||||
625,2.408
|
||||
626,2.593
|
||||
627,3.526
|
||||
628,2.554
|
||||
629,2.746
|
||||
630,2.684
|
||||
631,2.729
|
||||
632,2.762
|
||||
633,2.768
|
||||
634,3.034
|
||||
635,2.514
|
||||
636,2.732
|
||||
637,2.739
|
||||
638,2.692
|
||||
639,2.832
|
||||
640,2.810
|
||||
641,2.484
|
||||
642,2.632
|
||||
643,3.284
|
||||
644,2.311
|
||||
645,3.079
|
||||
646,2.656
|
||||
647,2.683
|
||||
648,2.622
|
||||
649,2.668
|
||||
650,2.779
|
||||
651,2.796
|
||||
652,2.874
|
||||
653,2.427
|
||||
654,2.644
|
||||
655,2.872
|
||||
656,2.867
|
||||
657,2.404
|
||||
658,2.917
|
||||
659,2.773
|
||||
660,2.857
|
||||
661,2.789
|
||||
662,2.443
|
||||
663,2.651
|
||||
664,2.839
|
||||
665,2.814
|
||||
666,2.575
|
||||
667,2.796
|
||||
668,2.709
|
||||
669,2.873
|
||||
670,2.670
|
||||
671,2.936
|
||||
672,2.496
|
||||
673,2.608
|
||||
674,2.641
|
||||
675,2.793
|
||||
676,2.765
|
||||
677,2.736
|
||||
678,2.679
|
||||
679,2.987
|
||||
680,2.884
|
||||
681,2.469
|
||||
682,2.774
|
||||
683,2.948
|
||||
684,2.598
|
||||
685,3.478
|
||||
686,2.856
|
||||
687,4.623
|
||||
688,3.038
|
||||
689,2.507
|
||||
690,2.717
|
||||
691,2.682
|
||||
692,2.807
|
||||
693,2.778
|
||||
694,2.699
|
||||
695,2.802
|
||||
696,2.605
|
||||
697,2.650
|
||||
698,2.789
|
||||
699,2.791
|
||||
700,3.015
|
||||
701,2.539
|
||||
702,2.790
|
||||
703,2.698
|
||||
704,2.637
|
||||
705,2.657
|
||||
706,2.800
|
||||
707,2.720
|
||||
708,2.702
|
||||
709,2.845
|
||||
710,2.583
|
||||
711,2.780
|
||||
712,2.782
|
||||
713,2.593
|
||||
714,2.695
|
||||
715,2.747
|
||||
716,2.871
|
||||
717,2.671
|
||||
718,2.676
|
||||
719,2.991
|
||||
720,2.727
|
||||
721,2.576
|
||||
722,2.539
|
||||
723,2.770
|
||||
724,2.712
|
||||
725,2.818
|
||||
726,2.615
|
||||
|
@@ -0,0 +1,723 @@
|
||||
frame,ms
|
||||
0,2.607
|
||||
1,2.532
|
||||
2,2.324
|
||||
3,3.432
|
||||
4,2.808
|
||||
5,2.870
|
||||
6,2.367
|
||||
7,2.311
|
||||
8,2.756
|
||||
9,3.278
|
||||
10,2.418
|
||||
11,2.665
|
||||
12,2.438
|
||||
13,2.646
|
||||
14,2.461
|
||||
15,2.941
|
||||
16,2.975
|
||||
17,3.164
|
||||
18,2.766
|
||||
19,2.341
|
||||
20,2.543
|
||||
21,3.395
|
||||
22,2.772
|
||||
23,2.575
|
||||
24,2.737
|
||||
25,2.427
|
||||
26,2.538
|
||||
27,2.708
|
||||
28,3.183
|
||||
29,2.734
|
||||
30,2.425
|
||||
31,2.546
|
||||
32,2.600
|
||||
33,2.545
|
||||
34,3.705
|
||||
35,2.563
|
||||
36,2.907
|
||||
37,2.368
|
||||
38,2.470
|
||||
39,2.411
|
||||
40,3.778
|
||||
41,3.735
|
||||
42,2.292
|
||||
43,2.458
|
||||
44,2.566
|
||||
45,3.044
|
||||
46,3.449
|
||||
47,2.914
|
||||
48,3.243
|
||||
49,2.084
|
||||
50,3.872
|
||||
51,1.633
|
||||
52,3.389
|
||||
53,2.448
|
||||
54,2.474
|
||||
55,2.509
|
||||
56,2.346
|
||||
57,3.192
|
||||
58,2.762
|
||||
59,2.788
|
||||
60,2.538
|
||||
61,2.520
|
||||
62,2.517
|
||||
63,3.036
|
||||
64,3.205
|
||||
65,3.080
|
||||
66,2.514
|
||||
67,2.429
|
||||
68,2.515
|
||||
69,2.963
|
||||
70,3.555
|
||||
71,2.430
|
||||
72,2.742
|
||||
73,2.435
|
||||
74,2.526
|
||||
75,3.304
|
||||
76,3.511
|
||||
77,2.477
|
||||
78,2.472
|
||||
79,2.452
|
||||
80,2.421
|
||||
81,3.334
|
||||
82,3.286
|
||||
83,2.725
|
||||
84,2.411
|
||||
85,2.597
|
||||
86,2.440
|
||||
87,3.213
|
||||
88,3.411
|
||||
89,3.047
|
||||
90,2.365
|
||||
91,2.530
|
||||
92,2.877
|
||||
93,3.249
|
||||
94,3.186
|
||||
95,2.684
|
||||
96,2.304
|
||||
97,2.406
|
||||
98,2.948
|
||||
99,3.652
|
||||
100,2.822
|
||||
101,2.552
|
||||
102,2.575
|
||||
103,2.354
|
||||
104,2.676
|
||||
105,2.775
|
||||
106,3.865
|
||||
107,2.480
|
||||
108,2.501
|
||||
109,2.675
|
||||
110,2.724
|
||||
111,3.671
|
||||
112,2.828
|
||||
113,3.064
|
||||
114,2.153
|
||||
115,2.752
|
||||
116,2.936
|
||||
117,3.008
|
||||
118,2.932
|
||||
119,2.648
|
||||
120,2.441
|
||||
121,3.471
|
||||
122,1.885
|
||||
123,4.435
|
||||
124,2.504
|
||||
125,3.241
|
||||
126,2.813
|
||||
127,2.445
|
||||
128,3.321
|
||||
129,3.055
|
||||
130,3.222
|
||||
131,2.766
|
||||
132,2.582
|
||||
133,2.597
|
||||
134,3.637
|
||||
135,3.150
|
||||
136,3.163
|
||||
137,2.675
|
||||
138,2.708
|
||||
139,2.581
|
||||
140,3.377
|
||||
141,3.518
|
||||
142,2.565
|
||||
143,2.385
|
||||
144,2.360
|
||||
145,2.768
|
||||
146,3.172
|
||||
147,2.754
|
||||
148,2.758
|
||||
149,2.474
|
||||
150,2.995
|
||||
151,2.903
|
||||
152,3.398
|
||||
153,2.484
|
||||
154,2.894
|
||||
155,3.022
|
||||
156,2.413
|
||||
157,3.517
|
||||
158,3.370
|
||||
159,2.854
|
||||
160,2.485
|
||||
161,3.058
|
||||
162,2.473
|
||||
163,3.390
|
||||
164,3.023
|
||||
165,2.543
|
||||
166,2.629
|
||||
167,2.929
|
||||
168,2.928
|
||||
169,3.268
|
||||
170,2.622
|
||||
171,2.764
|
||||
172,2.920
|
||||
173,2.681
|
||||
174,2.551
|
||||
175,3.492
|
||||
176,2.534
|
||||
177,2.741
|
||||
178,2.949
|
||||
179,2.554
|
||||
180,2.596
|
||||
181,3.522
|
||||
182,3.207
|
||||
183,2.722
|
||||
184,2.945
|
||||
185,2.474
|
||||
186,2.934
|
||||
187,3.447
|
||||
188,2.515
|
||||
189,2.317
|
||||
190,3.059
|
||||
191,2.542
|
||||
192,3.417
|
||||
193,2.737
|
||||
194,2.456
|
||||
195,3.025
|
||||
196,2.640
|
||||
197,2.601
|
||||
198,3.567
|
||||
199,2.350
|
||||
200,2.581
|
||||
201,3.079
|
||||
202,2.514
|
||||
203,2.462
|
||||
204,3.725
|
||||
205,3.730
|
||||
206,2.670
|
||||
207,2.308
|
||||
208,2.689
|
||||
209,2.565
|
||||
210,3.479
|
||||
211,2.604
|
||||
212,3.339
|
||||
213,2.373
|
||||
214,2.678
|
||||
215,3.340
|
||||
216,2.867
|
||||
217,2.751
|
||||
218,2.747
|
||||
219,2.410
|
||||
220,2.485
|
||||
221,2.594
|
||||
222,3.602
|
||||
223,2.689
|
||||
224,2.565
|
||||
225,2.397
|
||||
226,2.562
|
||||
227,2.598
|
||||
228,3.403
|
||||
229,3.303
|
||||
230,2.775
|
||||
231,2.101
|
||||
232,2.499
|
||||
233,2.991
|
||||
234,3.059
|
||||
235,3.210
|
||||
236,2.568
|
||||
237,2.066
|
||||
238,2.498
|
||||
239,2.572
|
||||
240,3.281
|
||||
241,2.676
|
||||
242,2.640
|
||||
243,2.867
|
||||
244,2.418
|
||||
245,2.493
|
||||
246,3.126
|
||||
247,2.845
|
||||
248,2.522
|
||||
249,2.459
|
||||
250,2.524
|
||||
251,2.466
|
||||
252,3.060
|
||||
253,3.848
|
||||
254,2.516
|
||||
255,2.460
|
||||
256,2.499
|
||||
257,2.427
|
||||
258,3.463
|
||||
259,2.761
|
||||
260,2.674
|
||||
261,2.450
|
||||
262,2.491
|
||||
263,2.543
|
||||
264,2.732
|
||||
265,3.679
|
||||
266,2.325
|
||||
267,2.529
|
||||
268,2.344
|
||||
269,2.534
|
||||
270,2.691
|
||||
271,3.176
|
||||
272,2.492
|
||||
273,2.721
|
||||
274,2.587
|
||||
275,2.594
|
||||
276,2.326
|
||||
277,3.434
|
||||
278,3.514
|
||||
279,2.415
|
||||
280,2.481
|
||||
281,2.478
|
||||
282,2.555
|
||||
283,3.303
|
||||
284,3.098
|
||||
285,2.451
|
||||
286,2.552
|
||||
287,2.413
|
||||
288,2.613
|
||||
289,3.194
|
||||
290,3.191
|
||||
291,2.338
|
||||
292,2.561
|
||||
293,2.785
|
||||
294,2.240
|
||||
295,3.324
|
||||
296,2.767
|
||||
297,2.589
|
||||
298,2.608
|
||||
299,2.432
|
||||
300,2.444
|
||||
301,2.968
|
||||
302,3.792
|
||||
303,2.590
|
||||
304,2.335
|
||||
305,2.520
|
||||
306,2.435
|
||||
307,3.613
|
||||
308,2.681
|
||||
309,2.692
|
||||
310,2.329
|
||||
311,2.580
|
||||
312,2.355
|
||||
313,3.084
|
||||
314,3.026
|
||||
315,2.551
|
||||
316,2.490
|
||||
317,2.481
|
||||
318,2.553
|
||||
319,2.619
|
||||
320,3.350
|
||||
321,2.885
|
||||
322,2.576
|
||||
323,2.456
|
||||
324,2.441
|
||||
325,3.223
|
||||
326,3.031
|
||||
327,3.055
|
||||
328,2.592
|
||||
329,2.499
|
||||
330,2.354
|
||||
331,2.573
|
||||
332,3.323
|
||||
333,3.357
|
||||
334,2.241
|
||||
335,2.319
|
||||
336,2.472
|
||||
337,2.609
|
||||
338,3.565
|
||||
339,2.607
|
||||
340,2.539
|
||||
341,2.440
|
||||
342,2.483
|
||||
343,2.626
|
||||
344,3.407
|
||||
345,2.772
|
||||
346,2.491
|
||||
347,2.456
|
||||
348,2.553
|
||||
349,2.773
|
||||
350,3.239
|
||||
351,4.079
|
||||
352,2.346
|
||||
353,2.177
|
||||
354,2.562
|
||||
355,2.350
|
||||
356,3.329
|
||||
357,3.383
|
||||
358,2.441
|
||||
359,2.399
|
||||
360,2.530
|
||||
361,2.455
|
||||
362,3.219
|
||||
363,2.879
|
||||
364,2.726
|
||||
365,2.418
|
||||
366,2.511
|
||||
367,2.544
|
||||
368,3.077
|
||||
369,2.926
|
||||
370,2.571
|
||||
371,2.595
|
||||
372,2.534
|
||||
373,2.367
|
||||
374,2.671
|
||||
375,4.222
|
||||
376,2.411
|
||||
377,2.569
|
||||
378,2.493
|
||||
379,2.464
|
||||
380,2.691
|
||||
381,3.474
|
||||
382,2.697
|
||||
383,2.541
|
||||
384,2.494
|
||||
385,2.476
|
||||
386,2.473
|
||||
387,3.464
|
||||
388,3.084
|
||||
389,2.144
|
||||
390,2.503
|
||||
391,2.501
|
||||
392,2.525
|
||||
393,3.335
|
||||
394,2.919
|
||||
395,2.334
|
||||
396,2.557
|
||||
397,2.579
|
||||
398,2.567
|
||||
399,4.120
|
||||
400,3.297
|
||||
401,2.561
|
||||
402,2.428
|
||||
403,2.495
|
||||
404,2.729
|
||||
405,3.752
|
||||
406,2.783
|
||||
407,2.509
|
||||
408,2.337
|
||||
409,2.589
|
||||
410,2.556
|
||||
411,3.449
|
||||
412,2.702
|
||||
413,2.742
|
||||
414,2.384
|
||||
415,2.759
|
||||
416,2.290
|
||||
417,3.374
|
||||
418,2.934
|
||||
419,2.546
|
||||
420,2.228
|
||||
421,2.672
|
||||
422,2.369
|
||||
423,3.442
|
||||
424,3.484
|
||||
425,2.621
|
||||
426,2.279
|
||||
427,2.529
|
||||
428,2.889
|
||||
429,2.910
|
||||
430,2.981
|
||||
431,2.957
|
||||
432,2.420
|
||||
433,2.398
|
||||
434,2.310
|
||||
435,3.662
|
||||
436,2.422
|
||||
437,2.676
|
||||
438,2.557
|
||||
439,2.527
|
||||
440,2.564
|
||||
441,2.649
|
||||
442,3.168
|
||||
443,2.499
|
||||
444,2.737
|
||||
445,2.637
|
||||
446,2.509
|
||||
447,2.716
|
||||
448,3.845
|
||||
449,2.654
|
||||
450,2.536
|
||||
451,2.366
|
||||
452,2.526
|
||||
453,2.906
|
||||
454,3.101
|
||||
455,2.809
|
||||
456,2.466
|
||||
457,2.502
|
||||
458,2.767
|
||||
459,2.486
|
||||
460,3.137
|
||||
461,3.402
|
||||
462,2.187
|
||||
463,2.398
|
||||
464,2.392
|
||||
465,2.614
|
||||
466,3.371
|
||||
467,2.847
|
||||
468,2.463
|
||||
469,2.639
|
||||
470,2.288
|
||||
471,2.562
|
||||
472,3.381
|
||||
473,3.491
|
||||
474,2.521
|
||||
475,2.397
|
||||
476,2.431
|
||||
477,2.759
|
||||
478,3.222
|
||||
479,2.957
|
||||
480,2.470
|
||||
481,2.403
|
||||
482,2.560
|
||||
483,2.470
|
||||
484,3.451
|
||||
485,2.905
|
||||
486,2.583
|
||||
487,2.329
|
||||
488,2.798
|
||||
489,2.289
|
||||
490,2.882
|
||||
491,2.903
|
||||
492,2.627
|
||||
493,2.897
|
||||
494,2.321
|
||||
495,2.472
|
||||
496,2.882
|
||||
497,3.844
|
||||
498,3.096
|
||||
499,2.525
|
||||
500,2.250
|
||||
501,2.477
|
||||
502,2.728
|
||||
503,3.391
|
||||
504,2.539
|
||||
505,2.635
|
||||
506,2.542
|
||||
507,2.435
|
||||
508,3.011
|
||||
509,3.042
|
||||
510,2.965
|
||||
511,2.273
|
||||
512,2.433
|
||||
513,3.020
|
||||
514,2.776
|
||||
515,3.646
|
||||
516,2.856
|
||||
517,2.886
|
||||
518,2.522
|
||||
519,2.528
|
||||
520,4.396
|
||||
521,3.397
|
||||
522,2.218
|
||||
523,2.433
|
||||
524,2.605
|
||||
525,3.504
|
||||
526,3.087
|
||||
527,3.186
|
||||
528,2.343
|
||||
529,2.433
|
||||
530,2.900
|
||||
531,3.000
|
||||
532,3.511
|
||||
533,2.663
|
||||
534,2.699
|
||||
535,2.423
|
||||
536,3.136
|
||||
537,2.802
|
||||
538,3.151
|
||||
539,2.507
|
||||
540,2.973
|
||||
541,2.325
|
||||
542,3.322
|
||||
543,3.260
|
||||
544,3.525
|
||||
545,2.846
|
||||
546,2.125
|
||||
547,2.697
|
||||
548,2.829
|
||||
549,3.389
|
||||
550,2.758
|
||||
551,2.866
|
||||
552,2.338
|
||||
553,3.115
|
||||
554,2.500
|
||||
555,3.387
|
||||
556,2.851
|
||||
557,2.511
|
||||
558,2.459
|
||||
559,3.107
|
||||
560,2.521
|
||||
561,3.308
|
||||
562,2.782
|
||||
563,2.810
|
||||
564,2.323
|
||||
565,3.259
|
||||
566,2.366
|
||||
567,3.542
|
||||
568,2.929
|
||||
569,2.299
|
||||
570,2.336
|
||||
571,3.027
|
||||
572,2.540
|
||||
573,3.409
|
||||
574,2.784
|
||||
575,2.444
|
||||
576,2.437
|
||||
577,2.734
|
||||
578,2.591
|
||||
579,3.845
|
||||
580,3.212
|
||||
581,2.802
|
||||
582,2.370
|
||||
583,2.704
|
||||
584,2.624
|
||||
585,4.023
|
||||
586,2.360
|
||||
587,2.759
|
||||
588,2.511
|
||||
589,2.608
|
||||
590,2.922
|
||||
591,3.081
|
||||
592,3.271
|
||||
593,2.471
|
||||
594,2.516
|
||||
595,2.474
|
||||
596,2.814
|
||||
597,3.127
|
||||
598,2.537
|
||||
599,3.080
|
||||
600,2.341
|
||||
601,3.027
|
||||
602,2.766
|
||||
603,2.984
|
||||
604,2.450
|
||||
605,2.922
|
||||
606,2.481
|
||||
607,2.616
|
||||
608,2.541
|
||||
609,3.462
|
||||
610,2.971
|
||||
611,2.955
|
||||
612,2.258
|
||||
613,2.682
|
||||
614,2.770
|
||||
615,3.119
|
||||
616,2.792
|
||||
617,2.598
|
||||
618,2.260
|
||||
619,2.441
|
||||
620,2.536
|
||||
621,3.425
|
||||
622,2.958
|
||||
623,2.722
|
||||
624,2.099
|
||||
625,2.400
|
||||
626,2.570
|
||||
627,3.295
|
||||
628,2.919
|
||||
629,2.737
|
||||
630,2.311
|
||||
631,2.408
|
||||
632,2.514
|
||||
633,3.325
|
||||
634,2.767
|
||||
635,2.724
|
||||
636,2.288
|
||||
637,2.680
|
||||
638,2.347
|
||||
639,2.902
|
||||
640,3.058
|
||||
641,2.770
|
||||
642,2.522
|
||||
643,2.406
|
||||
644,2.539
|
||||
645,2.753
|
||||
646,3.215
|
||||
647,2.636
|
||||
648,2.639
|
||||
649,2.666
|
||||
650,2.392
|
||||
651,2.408
|
||||
652,3.515
|
||||
653,2.687
|
||||
654,2.589
|
||||
655,2.428
|
||||
656,2.375
|
||||
657,2.619
|
||||
658,3.308
|
||||
659,2.664
|
||||
660,2.752
|
||||
661,2.456
|
||||
662,2.365
|
||||
663,2.533
|
||||
664,3.354
|
||||
665,2.812
|
||||
666,2.684
|
||||
667,2.349
|
||||
668,2.441
|
||||
669,2.714
|
||||
670,2.769
|
||||
671,3.368
|
||||
672,2.624
|
||||
673,2.308
|
||||
674,2.365
|
||||
675,2.634
|
||||
676,2.617
|
||||
677,3.350
|
||||
678,2.733
|
||||
679,2.623
|
||||
680,2.462
|
||||
681,2.494
|
||||
682,2.460
|
||||
683,3.463
|
||||
684,2.537
|
||||
685,2.628
|
||||
686,2.674
|
||||
687,2.381
|
||||
688,2.516
|
||||
689,3.586
|
||||
690,2.681
|
||||
691,2.676
|
||||
692,2.289
|
||||
693,2.726
|
||||
694,2.303
|
||||
695,2.934
|
||||
696,3.339
|
||||
697,2.606
|
||||
698,2.187
|
||||
699,2.687
|
||||
700,2.328
|
||||
701,3.062
|
||||
702,2.930
|
||||
703,2.871
|
||||
704,2.313
|
||||
705,2.582
|
||||
706,2.464
|
||||
707,2.521
|
||||
708,3.304
|
||||
709,2.492
|
||||
710,2.723
|
||||
711,2.587
|
||||
712,2.608
|
||||
713,2.574
|
||||
714,4.002
|
||||
715,3.011
|
||||
716,2.227
|
||||
717,2.374
|
||||
718,2.343
|
||||
719,2.541
|
||||
720,3.580
|
||||
721,2.854
|
||||
|
@@ -0,0 +1,760 @@
|
||||
frame,ms
|
||||
0,2.428
|
||||
1,2.633
|
||||
2,2.238
|
||||
3,3.606
|
||||
4,2.431
|
||||
5,2.649
|
||||
6,4.050
|
||||
7,2.011
|
||||
8,2.735
|
||||
9,3.390
|
||||
10,2.901
|
||||
11,2.237
|
||||
12,2.686
|
||||
13,2.383
|
||||
14,2.451
|
||||
15,3.412
|
||||
16,2.458
|
||||
17,2.474
|
||||
18,2.588
|
||||
19,2.556
|
||||
20,2.478
|
||||
21,3.150
|
||||
22,2.667
|
||||
23,2.349
|
||||
24,2.568
|
||||
25,2.580
|
||||
26,2.393
|
||||
27,2.575
|
||||
28,3.132
|
||||
29,2.536
|
||||
30,2.508
|
||||
31,2.710
|
||||
32,2.348
|
||||
33,2.537
|
||||
34,2.820
|
||||
35,3.162
|
||||
36,2.163
|
||||
37,2.562
|
||||
38,2.619
|
||||
39,2.323
|
||||
40,2.628
|
||||
41,3.075
|
||||
42,2.512
|
||||
43,2.448
|
||||
44,2.505
|
||||
45,2.454
|
||||
46,2.607
|
||||
47,3.128
|
||||
48,2.476
|
||||
49,2.562
|
||||
50,2.434
|
||||
51,3.385
|
||||
52,2.076
|
||||
53,3.435
|
||||
54,2.672
|
||||
55,2.550
|
||||
56,1.998
|
||||
57,2.583
|
||||
58,2.518
|
||||
59,2.777
|
||||
60,3.920
|
||||
61,2.934
|
||||
62,2.675
|
||||
63,2.320
|
||||
64,2.309
|
||||
65,2.520
|
||||
66,3.947
|
||||
67,2.596
|
||||
68,2.708
|
||||
69,2.291
|
||||
70,2.404
|
||||
71,2.551
|
||||
72,3.715
|
||||
73,2.100
|
||||
74,2.539
|
||||
75,2.682
|
||||
76,2.273
|
||||
77,2.442
|
||||
78,3.952
|
||||
79,2.233
|
||||
80,2.374
|
||||
81,2.686
|
||||
82,2.407
|
||||
83,2.654
|
||||
84,3.613
|
||||
85,2.320
|
||||
86,2.685
|
||||
87,2.744
|
||||
88,2.357
|
||||
89,2.565
|
||||
90,3.756
|
||||
91,2.672
|
||||
92,2.128
|
||||
93,2.583
|
||||
94,2.546
|
||||
95,2.391
|
||||
96,2.935
|
||||
97,3.624
|
||||
98,1.934
|
||||
99,2.509
|
||||
100,2.603
|
||||
101,2.449
|
||||
102,2.802
|
||||
103,3.678
|
||||
104,2.336
|
||||
105,2.509
|
||||
106,2.551
|
||||
107,2.576
|
||||
108,2.997
|
||||
109,3.208
|
||||
110,2.301
|
||||
111,2.359
|
||||
112,2.484
|
||||
113,2.646
|
||||
114,2.801
|
||||
115,3.226
|
||||
116,2.635
|
||||
117,2.325
|
||||
118,2.501
|
||||
119,2.602
|
||||
120,2.564
|
||||
121,3.151
|
||||
122,3.149
|
||||
123,2.235
|
||||
124,2.596
|
||||
125,2.432
|
||||
126,2.665
|
||||
127,2.648
|
||||
128,3.884
|
||||
129,2.087
|
||||
130,2.375
|
||||
131,2.586
|
||||
132,2.734
|
||||
133,2.338
|
||||
134,3.617
|
||||
135,2.418
|
||||
136,2.350
|
||||
137,2.713
|
||||
138,2.758
|
||||
139,2.239
|
||||
140,3.585
|
||||
141,2.565
|
||||
142,2.787
|
||||
143,2.115
|
||||
144,2.435
|
||||
145,2.489
|
||||
146,3.062
|
||||
147,2.958
|
||||
148,2.402
|
||||
149,2.425
|
||||
150,2.516
|
||||
151,2.505
|
||||
152,2.543
|
||||
153,3.690
|
||||
154,2.060
|
||||
155,2.510
|
||||
156,2.428
|
||||
157,2.603
|
||||
158,2.491
|
||||
159,3.178
|
||||
160,2.496
|
||||
161,2.424
|
||||
162,2.495
|
||||
163,2.667
|
||||
164,2.434
|
||||
165,3.040
|
||||
166,2.949
|
||||
167,2.094
|
||||
168,2.510
|
||||
169,2.586
|
||||
170,2.395
|
||||
171,3.004
|
||||
172,3.009
|
||||
173,2.288
|
||||
174,2.548
|
||||
175,2.337
|
||||
176,2.598
|
||||
177,2.498
|
||||
178,3.526
|
||||
179,2.533
|
||||
180,2.134
|
||||
181,2.560
|
||||
182,2.541
|
||||
183,2.436
|
||||
184,2.635
|
||||
185,3.361
|
||||
186,2.261
|
||||
187,2.566
|
||||
188,2.383
|
||||
189,2.529
|
||||
190,2.645
|
||||
191,3.190
|
||||
192,2.523
|
||||
193,2.523
|
||||
194,2.346
|
||||
195,2.865
|
||||
196,2.348
|
||||
197,2.879
|
||||
198,3.657
|
||||
199,1.824
|
||||
200,2.551
|
||||
201,2.449
|
||||
202,2.607
|
||||
203,3.085
|
||||
204,3.163
|
||||
205,2.404
|
||||
206,2.327
|
||||
207,2.553
|
||||
208,2.576
|
||||
209,2.544
|
||||
210,3.193
|
||||
211,2.647
|
||||
212,2.332
|
||||
213,2.544
|
||||
214,2.767
|
||||
215,2.429
|
||||
216,3.103
|
||||
217,2.861
|
||||
218,2.318
|
||||
219,2.552
|
||||
220,2.587
|
||||
221,2.352
|
||||
222,3.128
|
||||
223,2.621
|
||||
224,2.461
|
||||
225,2.422
|
||||
226,2.601
|
||||
227,2.748
|
||||
228,2.545
|
||||
229,3.224
|
||||
230,2.206
|
||||
231,2.459
|
||||
232,2.530
|
||||
233,2.461
|
||||
234,2.570
|
||||
235,3.271
|
||||
236,2.405
|
||||
237,2.424
|
||||
238,2.671
|
||||
239,2.450
|
||||
240,2.644
|
||||
241,2.510
|
||||
242,3.023
|
||||
243,2.461
|
||||
244,2.618
|
||||
245,2.350
|
||||
246,2.551
|
||||
247,2.472
|
||||
248,3.642
|
||||
249,2.576
|
||||
250,2.548
|
||||
251,3.599
|
||||
252,1.992
|
||||
253,2.424
|
||||
254,3.752
|
||||
255,2.241
|
||||
256,2.360
|
||||
257,2.522
|
||||
258,2.584
|
||||
259,2.653
|
||||
260,2.871
|
||||
261,2.827
|
||||
262,2.369
|
||||
263,2.482
|
||||
264,2.599
|
||||
265,2.294
|
||||
266,2.686
|
||||
267,3.428
|
||||
268,2.240
|
||||
269,2.435
|
||||
270,2.468
|
||||
271,2.619
|
||||
272,2.519
|
||||
273,3.247
|
||||
274,2.539
|
||||
275,2.442
|
||||
276,2.518
|
||||
277,2.489
|
||||
278,2.463
|
||||
279,2.839
|
||||
280,2.907
|
||||
281,2.786
|
||||
282,2.211
|
||||
283,2.615
|
||||
284,2.378
|
||||
285,2.671
|
||||
286,3.219
|
||||
287,2.435
|
||||
288,2.509
|
||||
289,2.323
|
||||
290,2.502
|
||||
291,2.526
|
||||
292,3.292
|
||||
293,2.468
|
||||
294,2.526
|
||||
295,2.500
|
||||
296,2.431
|
||||
297,2.481
|
||||
298,2.921
|
||||
299,3.009
|
||||
300,2.415
|
||||
301,2.600
|
||||
302,2.344
|
||||
303,2.533
|
||||
304,2.618
|
||||
305,3.458
|
||||
306,2.455
|
||||
307,2.469
|
||||
308,2.495
|
||||
309,2.568
|
||||
310,2.473
|
||||
311,3.179
|
||||
312,2.694
|
||||
313,2.503
|
||||
314,2.587
|
||||
315,2.317
|
||||
316,2.609
|
||||
317,2.590
|
||||
318,3.211
|
||||
319,2.392
|
||||
320,2.490
|
||||
321,2.515
|
||||
322,2.496
|
||||
323,2.483
|
||||
324,3.157
|
||||
325,2.540
|
||||
326,2.422
|
||||
327,2.574
|
||||
328,2.468
|
||||
329,2.547
|
||||
330,2.788
|
||||
331,3.075
|
||||
332,2.231
|
||||
333,2.537
|
||||
334,2.577
|
||||
335,2.584
|
||||
336,2.473
|
||||
337,3.415
|
||||
338,2.126
|
||||
339,2.506
|
||||
340,2.374
|
||||
341,2.721
|
||||
342,2.418
|
||||
343,3.259
|
||||
344,2.509
|
||||
345,2.519
|
||||
346,2.436
|
||||
347,2.472
|
||||
348,2.504
|
||||
349,2.858
|
||||
350,3.250
|
||||
351,2.161
|
||||
352,2.586
|
||||
353,2.376
|
||||
354,2.651
|
||||
355,2.504
|
||||
356,3.399
|
||||
357,2.353
|
||||
358,2.381
|
||||
359,2.513
|
||||
360,2.633
|
||||
361,2.566
|
||||
362,3.615
|
||||
363,2.593
|
||||
364,2.084
|
||||
365,2.411
|
||||
366,2.653
|
||||
367,2.521
|
||||
368,2.825
|
||||
369,2.891
|
||||
370,2.576
|
||||
371,2.302
|
||||
372,2.582
|
||||
373,2.513
|
||||
374,2.598
|
||||
375,3.790
|
||||
376,2.352
|
||||
377,2.306
|
||||
378,2.434
|
||||
379,2.514
|
||||
380,2.708
|
||||
381,2.978
|
||||
382,2.813
|
||||
383,2.327
|
||||
384,2.502
|
||||
385,2.446
|
||||
386,2.605
|
||||
387,3.033
|
||||
388,3.091
|
||||
389,2.206
|
||||
390,2.648
|
||||
391,2.732
|
||||
392,2.296
|
||||
393,3.406
|
||||
394,2.490
|
||||
395,2.424
|
||||
396,2.443
|
||||
397,2.601
|
||||
398,2.628
|
||||
399,2.477
|
||||
400,3.241
|
||||
401,2.573
|
||||
402,2.422
|
||||
403,2.384
|
||||
404,2.505
|
||||
405,2.575
|
||||
406,2.909
|
||||
407,2.897
|
||||
408,2.486
|
||||
409,2.431
|
||||
410,2.691
|
||||
411,2.342
|
||||
412,2.733
|
||||
413,3.518
|
||||
414,2.391
|
||||
415,2.160
|
||||
416,2.671
|
||||
417,2.505
|
||||
418,2.241
|
||||
419,3.355
|
||||
420,2.775
|
||||
421,2.395
|
||||
422,2.864
|
||||
423,2.290
|
||||
424,2.469
|
||||
425,2.977
|
||||
426,2.759
|
||||
427,2.460
|
||||
428,2.456
|
||||
429,2.497
|
||||
430,2.362
|
||||
431,2.874
|
||||
432,2.812
|
||||
433,2.559
|
||||
434,2.283
|
||||
435,2.670
|
||||
436,2.482
|
||||
437,2.495
|
||||
438,3.118
|
||||
439,3.018
|
||||
440,2.252
|
||||
441,3.222
|
||||
442,2.483
|
||||
443,2.436
|
||||
444,3.209
|
||||
445,3.169
|
||||
446,2.455
|
||||
447,2.432
|
||||
448,2.464
|
||||
449,2.500
|
||||
450,2.967
|
||||
451,2.958
|
||||
452,2.390
|
||||
453,2.494
|
||||
454,2.523
|
||||
455,2.473
|
||||
456,2.516
|
||||
457,3.527
|
||||
458,2.262
|
||||
459,2.377
|
||||
460,2.840
|
||||
461,2.135
|
||||
462,2.520
|
||||
463,3.285
|
||||
464,2.940
|
||||
465,2.256
|
||||
466,2.496
|
||||
467,2.705
|
||||
468,2.395
|
||||
469,3.031
|
||||
470,3.104
|
||||
471,2.411
|
||||
472,2.630
|
||||
473,2.468
|
||||
474,2.416
|
||||
475,2.559
|
||||
476,2.732
|
||||
477,2.441
|
||||
478,2.624
|
||||
479,2.536
|
||||
480,2.458
|
||||
481,2.516
|
||||
482,2.507
|
||||
483,2.606
|
||||
484,2.616
|
||||
485,2.881
|
||||
486,2.358
|
||||
487,2.469
|
||||
488,2.513
|
||||
489,3.250
|
||||
490,2.606
|
||||
491,2.545
|
||||
492,2.407
|
||||
493,2.437
|
||||
494,2.663
|
||||
495,3.346
|
||||
496,2.349
|
||||
497,2.481
|
||||
498,2.613
|
||||
499,2.483
|
||||
500,2.417
|
||||
501,2.923
|
||||
502,3.059
|
||||
503,2.483
|
||||
504,2.373
|
||||
505,2.525
|
||||
506,2.530
|
||||
507,2.431
|
||||
508,2.917
|
||||
509,2.559
|
||||
510,2.623
|
||||
511,3.172
|
||||
512,2.304
|
||||
513,2.331
|
||||
514,3.287
|
||||
515,2.544
|
||||
516,2.429
|
||||
517,2.690
|
||||
518,2.464
|
||||
519,2.406
|
||||
520,2.804
|
||||
521,2.899
|
||||
522,2.461
|
||||
523,2.536
|
||||
524,2.414
|
||||
525,2.380
|
||||
526,2.616
|
||||
527,3.236
|
||||
528,2.499
|
||||
529,2.558
|
||||
530,2.584
|
||||
531,2.309
|
||||
532,2.447
|
||||
533,3.039
|
||||
534,3.133
|
||||
535,2.170
|
||||
536,2.583
|
||||
537,2.505
|
||||
538,2.666
|
||||
539,2.567
|
||||
540,3.307
|
||||
541,2.177
|
||||
542,2.495
|
||||
543,2.441
|
||||
544,2.638
|
||||
545,2.521
|
||||
546,3.527
|
||||
547,2.460
|
||||
548,2.362
|
||||
549,2.520
|
||||
550,2.439
|
||||
551,2.572
|
||||
552,3.087
|
||||
553,2.707
|
||||
554,2.475
|
||||
555,2.283
|
||||
556,2.604
|
||||
557,2.529
|
||||
558,3.094
|
||||
559,3.051
|
||||
560,2.085
|
||||
561,2.365
|
||||
562,2.711
|
||||
563,2.351
|
||||
564,2.572
|
||||
565,2.979
|
||||
566,2.805
|
||||
567,2.373
|
||||
568,2.744
|
||||
569,2.340
|
||||
570,2.285
|
||||
571,2.791
|
||||
572,3.447
|
||||
573,2.208
|
||||
574,2.518
|
||||
575,2.448
|
||||
576,3.917
|
||||
577,1.540
|
||||
578,2.923
|
||||
579,2.632
|
||||
580,2.404
|
||||
581,2.582
|
||||
582,2.508
|
||||
583,2.413
|
||||
584,3.602
|
||||
585,2.342
|
||||
586,2.482
|
||||
587,2.584
|
||||
588,3.062
|
||||
589,2.070
|
||||
590,2.726
|
||||
591,2.937
|
||||
592,2.385
|
||||
593,2.734
|
||||
594,2.188
|
||||
595,2.582
|
||||
596,2.487
|
||||
597,3.339
|
||||
598,2.535
|
||||
599,2.414
|
||||
600,2.459
|
||||
601,2.510
|
||||
602,2.547
|
||||
603,3.414
|
||||
604,2.314
|
||||
605,2.399
|
||||
606,2.550
|
||||
607,2.541
|
||||
608,2.355
|
||||
609,2.709
|
||||
610,3.067
|
||||
611,2.502
|
||||
612,2.467
|
||||
613,2.899
|
||||
614,2.177
|
||||
615,2.719
|
||||
616,3.049
|
||||
617,2.673
|
||||
618,2.259
|
||||
619,2.566
|
||||
620,2.383
|
||||
621,2.420
|
||||
622,3.318
|
||||
623,2.807
|
||||
624,2.348
|
||||
625,2.595
|
||||
626,2.433
|
||||
627,2.569
|
||||
628,2.620
|
||||
629,2.652
|
||||
630,3.140
|
||||
631,2.945
|
||||
632,2.571
|
||||
633,2.546
|
||||
634,2.428
|
||||
635,3.436
|
||||
636,2.393
|
||||
637,2.491
|
||||
638,2.511
|
||||
639,2.485
|
||||
640,2.603
|
||||
641,3.363
|
||||
642,2.442
|
||||
643,2.344
|
||||
644,2.462
|
||||
645,2.508
|
||||
646,2.617
|
||||
647,2.909
|
||||
648,2.824
|
||||
649,2.528
|
||||
650,2.424
|
||||
651,2.511
|
||||
652,2.517
|
||||
653,2.583
|
||||
654,3.331
|
||||
655,2.436
|
||||
656,2.455
|
||||
657,2.380
|
||||
658,2.570
|
||||
659,2.582
|
||||
660,2.789
|
||||
661,2.917
|
||||
662,2.526
|
||||
663,2.538
|
||||
664,2.447
|
||||
665,2.423
|
||||
666,2.695
|
||||
667,3.143
|
||||
668,2.616
|
||||
669,2.126
|
||||
670,2.596
|
||||
671,2.464
|
||||
672,3.092
|
||||
673,2.681
|
||||
674,2.641
|
||||
675,2.311
|
||||
676,2.551
|
||||
677,2.551
|
||||
678,2.837
|
||||
679,2.532
|
||||
680,3.006
|
||||
681,2.289
|
||||
682,2.521
|
||||
683,2.556
|
||||
684,2.593
|
||||
685,2.552
|
||||
686,3.173
|
||||
687,2.415
|
||||
688,2.429
|
||||
689,2.733
|
||||
690,2.286
|
||||
691,2.476
|
||||
692,3.256
|
||||
693,2.897
|
||||
694,2.528
|
||||
695,2.377
|
||||
696,2.518
|
||||
697,2.504
|
||||
698,3.074
|
||||
699,2.814
|
||||
700,2.433
|
||||
701,2.338
|
||||
702,2.476
|
||||
703,2.458
|
||||
704,3.141
|
||||
705,2.551
|
||||
706,2.717
|
||||
707,2.300
|
||||
708,2.527
|
||||
709,2.446
|
||||
710,2.469
|
||||
711,2.866
|
||||
712,2.776
|
||||
713,2.507
|
||||
714,2.457
|
||||
715,2.520
|
||||
716,2.450
|
||||
717,2.568
|
||||
718,3.192
|
||||
719,2.471
|
||||
720,2.790
|
||||
721,2.337
|
||||
722,2.548
|
||||
723,2.435
|
||||
724,3.313
|
||||
725,2.274
|
||||
726,2.461
|
||||
727,2.451
|
||||
728,2.556
|
||||
729,2.546
|
||||
730,2.869
|
||||
731,3.308
|
||||
732,2.293
|
||||
733,2.428
|
||||
734,2.686
|
||||
735,2.562
|
||||
736,2.523
|
||||
737,3.309
|
||||
738,2.311
|
||||
739,2.498
|
||||
740,2.485
|
||||
741,2.568
|
||||
742,2.392
|
||||
743,3.260
|
||||
744,2.534
|
||||
745,2.550
|
||||
746,2.355
|
||||
747,2.768
|
||||
748,2.319
|
||||
749,3.039
|
||||
750,2.721
|
||||
751,2.490
|
||||
752,2.378
|
||||
753,2.539
|
||||
754,2.823
|
||||
755,2.480
|
||||
756,3.533
|
||||
757,2.285
|
||||
758,2.365
|
||||
|
+33
-13
@@ -13,7 +13,19 @@ Record results in the table at the end of this file, with the date, the exact bu
|
||||
3. **Licensing.** Every new asset has provenance in `THIRD_PARTY_NOTICES.md`, or is recorded as unresolved and kept out of the build.
|
||||
4. **Version discipline.** Patch releases contain fixes, optimisation, docs, or tuning only — no new scope.
|
||||
|
||||
## Performance check
|
||||
## `0.1.0` checks
|
||||
|
||||
1. **Realism first.** Building placement, street width, sidewalk depth, material read and site identity match the reference photography. Compare side by side and note every deliberate deviation. "Looks right" is not evidence.
|
||||
2. Player traverses both the south-side frontage and the north-side frontage of the demo without falling through geometry, including the designated crossing. Walk the route twice: once normally, once with collision debug visualisation enabled.
|
||||
3. No blocking collision, no invisible walls, no gaps at sidewalk/road seams.
|
||||
4. Fresh checkout builds against the documented external engine installation.
|
||||
5. Linux build packaged and launched; controls documented in `Docs/` and shown in-game.
|
||||
6. **Performance last.** 60 FPS at 1440p per the performance check below — a floor every release meets, not the goal that shapes the content.
|
||||
7. Tag `v0.1.0`, write the `CHANGELOG.md` entry, then remove the completed section from `TODO.md` (its goal stays in `ROADMAP.md`).
|
||||
|
||||
## Performance check (last)
|
||||
|
||||
Performance is the **last** check on purpose: realism leads the design and the frame budget only decides how much of it fits. It is still a floor — a release is not tagged below 60 FPS at 1440p without the owner's explicit approval.
|
||||
|
||||
Measured at 1440p on the development workstation: Bazzite, Ryzen 7 7700X, 64 GB RAM, RTX 5070 Ti 16 GB.
|
||||
|
||||
@@ -22,16 +34,6 @@ Measured at 1440p on the development workstation: Bazzite, Ryzen 7 7700X, 64 GB
|
||||
- Target: 60 FPS at 1440p without frame generation.
|
||||
- Capture a frame-time graph over a fixed traversal route, not a single sample. State the route.
|
||||
|
||||
## `0.1.0` checks
|
||||
|
||||
1. Player traverses both the south-side frontage and the north-side frontage of the demo without falling through geometry, including the designated crossing. Walk the route twice: once normally, once with collision debug visualisation enabled.
|
||||
2. Building placement, street width, sidewalk depth, and site identity match the reference photography. Compare side by side and note every deliberate deviation. "Looks right" is not evidence.
|
||||
3. No blocking collision, no invisible walls, no gaps at sidewalk/road seams.
|
||||
4. 60 FPS at 1440p per the performance check above.
|
||||
5. Fresh checkout builds against the documented external engine installation.
|
||||
6. Linux build packaged and launched; controls documented in `Docs/` and shown in-game.
|
||||
7. Tag `v0.1.0`, write the `CHANGELOG.md` entry, then remove the completed section from `TODO.md` (its goal stays in `ROADMAP.md`).
|
||||
|
||||
## Check-record template
|
||||
|
||||
| Version | Check | Result | Evidence | Date |
|
||||
@@ -40,8 +42,10 @@ Measured at 1440p on the development workstation: Bazzite, Ryzen 7 7700X, 64 GB
|
||||
| `0.1.0` | Placement vs references | PASS (with deliberate simplifications) | Section below, 2026-11-08 | 2026-11-08 |
|
||||
| `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 | PASS — 2.42 ms avg (414 fps), p99 3.01 ms at 2560×1440, TSR on, VSync off, no FG | `-qaselftest` perf capture, `Docs/perf/0.1.0-walk-frametimes.csv` | 2026-11-08 |
|
||||
| `0.2.0` | 60 FPS at 1440p | PASS — 2.64 ms avg (379 fps), p50 2.54, p95 3.39, p99 3.76, max 4.05 ms at 2560×1440, TSR on, VSync off, no FG | `-qaselftest` perf capture, `Docs/perf/0.2.0-walk-frametimes.csv` | 2026-11-09 |
|
||||
| `0.2.0` | Debug overlay cost | PASS — same route with the F3 overlay drawn: 2.77 ms avg (361 fps), p50 2.64, p95 3.52, p99 3.87, max 4.43 ms; about **5 %** heavier, still 6× the floor. The overlay is on for `-perf`/`-qaselftest`, off in the built demo | `Docs/perf/0.2.0-walk-frametimes-overlay.csv` | 2026-11-09 |
|
||||
| `0.1.0` | Fresh checkout build | PASS — clean clone of `main` (`4bc41e9`), `git lfs pull` + `check-lfs.sh` OK (130 LFS files), both `GigLineEditor` and `GigLine` targets built with the documented engine | `/tmp/fresh-check`, 2026-11-08 | 2026-11-08 |
|
||||
| `0.1.0` | Packaged Linux build | PASS — packaged Development build launches straight into DemoMap, no missing-asset/linker errors; controls shown in-game and in `Docs/controls.md` | `Docs/perf/0.1.0-packaged-hud-controls.png`, 2026-11-08 | 2026-11-08 |
|
||||
| `0.1.0` | Packaged Linux build | PASS — packaged Development build launches straight into DemoMap, no missing-asset/linker errors; controls shown in-game and in `Docs/controls.md` | measurement recorded 2026-11-08; the capture was removed from the repository 2026-11-09 | 2026-11-08 |
|
||||
|
||||
## `0.1.0` — Placement vs references — record (2026-11-08)
|
||||
|
||||
@@ -125,6 +129,22 @@ Run on the packaged build (`GigLineQA`, Development, Vulkan SM5) with `-qaselfte
|
||||
|
||||
Committed frame-time series: `Docs/perf/0.1.0-walk-frametimes.csv`.
|
||||
|
||||
### `0.1.5` re-measure with the façade kit in the map
|
||||
|
||||
Same route, same preset, packaged Development build, **2560×1440**:
|
||||
|
||||
| Metric | `0.1.0` | `0.1.5` (kit + textures) |
|
||||
| ------- | ----------------: | -----------------------: |
|
||||
| Average | 2.42 ms (414 fps) | **2.76 ms (363 fps)** |
|
||||
| p50 | 2.38 ms | 2.73 ms |
|
||||
| p95 | 2.71 ms | 3.17 ms |
|
||||
| p99 | 3.01 ms | 3.64 ms |
|
||||
| Max | 4.00 ms | 4.62 ms |
|
||||
|
||||
The **625 kit actors and 8 generated textures cost about 12 %** of frame time and still leave **363 fps average, p99 3.64 ms** — roughly six times the 60 fps floor. Frame-time series: `Docs/perf/0.1.5-kit-walk-frametimes.csv`.
|
||||
|
||||
**Measurement trap, again:** runs that reported ~105 fps were two game instances overlapping (a previous launch had not exited). Confirm a single instance (`pgrep -af Binaries/Linux/GigLine`) before trusting a figure — 363 fps reproduced twice on a clean run.
|
||||
|
||||
## `0.1.0` — Fresh checkout build — record (2026-11-08)
|
||||
|
||||
Followed the documented steps in [`Docs/build.md`](build.md) exactly, into a clean directory with no prior state:
|
||||
@@ -157,6 +177,6 @@ Engine/Build/BatchFiles/Linux/Build.sh GigLine Linux Development \
|
||||
|
||||
- `Docs/controls.md` — full binding table (WASD, mouse look, Space, R, Esc) plus how to test input headlessly.
|
||||
- `AGigLineHUD` now draws a control legend bottom-right every frame: "WASD move · Mouse look · Space jump · R reset · Esc pause".
|
||||
- Verified in the packaged capture: the bottom-right text bbox is 426×12 px (a single right-aligned line), attributed text bottom-left. Screenshot: `Docs/perf/0.1.0-packaged-hud-controls.png`.
|
||||
- Measured in the packaged build on 2026-11-08: the bottom-right text bbox was 426×12 px (a single right-aligned line), attributed text bottom-left. The capture has since been removed from the repository, so the numbers stand as the record.
|
||||
|
||||
**Verdict: PASS.**
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,10 @@
|
||||
#include "GigLineCharacter.h"
|
||||
|
||||
#include "Animation/AnimSequence.h"
|
||||
#include "Animation/AnimSingleNodeInstance.h"
|
||||
#include "Camera/CameraComponent.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "GameFramework/SpringArmComponent.h"
|
||||
#include "Components/CapsuleComponent.h"
|
||||
#include "EnhancedInputComponent.h"
|
||||
#include "EnhancedInputSubsystems.h"
|
||||
@@ -11,6 +15,7 @@
|
||||
#include "Engine/World.h"
|
||||
#include "TimerManager.h"
|
||||
#include "Containers/Ticker.h"
|
||||
#include "HAL/IConsoleManager.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Misc/CommandLine.h"
|
||||
#include "Misc/Parse.h"
|
||||
@@ -28,7 +33,7 @@ static TAutoConsoleVariable<int32> CVarGigLineSetupLogs(
|
||||
|
||||
AGigLineCharacter::AGigLineCharacter()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = false;
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
// 1.75 m tall, 0.42 m wide, human scale in Unreal units (1 m = 100 uu).
|
||||
GetCapsuleComponent()->InitCapsuleSize(42.0f, 87.5f);
|
||||
@@ -51,9 +56,59 @@ AGigLineCharacter::AGigLineCharacter()
|
||||
FirstPersonCamera->SetRelativeLocation(FVector(0.0f, 0.0f, 72.5f)); // eye height 1.60 m
|
||||
FirstPersonCamera->bUsePawnControlRotation = true;
|
||||
|
||||
// An eye-level mesh is not needed for 0.1.0; the body is a capsule with a camera.
|
||||
GetMesh()->SetHiddenInGame(true);
|
||||
// Visible body (0.2.0). The animated import carries its own skeleton, so the
|
||||
// clips resolve without a retarget step; it is hidden while the first-person
|
||||
// camera is active and shown in third person.
|
||||
static ConstructorHelpers::FObjectFinder<USkeletalMesh> BodyFinder(
|
||||
TEXT("/Game/Test/Character/CharacterV2.CharacterV2"));
|
||||
if (BodyFinder.Succeeded())
|
||||
{
|
||||
GetMesh()->SetSkeletalMesh(BodyFinder.Object);
|
||||
}
|
||||
// The animated import carries no material of its own; without this the body is
|
||||
// grey clay. The warm skin material was created by the character import.
|
||||
static ConstructorHelpers::FObjectFinder<UMaterialInterface> SkinFinder(
|
||||
TEXT("/Game/Test/Character/M_Character_Export.M_Character_Export"));
|
||||
if (SkinFinder.Succeeded())
|
||||
{
|
||||
GetMesh()->SetMaterial(0, SkinFinder.Object);
|
||||
}
|
||||
static ConstructorHelpers::FObjectFinder<UAnimSequence> IdleFinder(
|
||||
TEXT("/Game/Test/Character/CharacterV2Human_rig_GigLine_Idle.CharacterV2Human_rig_GigLine_Idle"));
|
||||
if (IdleFinder.Succeeded())
|
||||
{
|
||||
IdleAnimation = IdleFinder.Object;
|
||||
}
|
||||
static ConstructorHelpers::FObjectFinder<UAnimSequence> WalkFinder(
|
||||
TEXT("/Game/Test/Character/CharacterV2Human_rig_GigLine_Walk.CharacterV2Human_rig_GigLine_Walk"));
|
||||
if (WalkFinder.Succeeded())
|
||||
{
|
||||
WalkAnimation = WalkFinder.Object;
|
||||
}
|
||||
GetMesh()->SetRelativeLocation(FVector(0.0f, 0.0f, -87.5f));
|
||||
GetMesh()->SetRelativeRotation(FRotator(0.0f, -90.0f, 0.0f));
|
||||
GetMesh()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
||||
GetMesh()->SetHiddenInGame(true);
|
||||
|
||||
// Third-person boom. bDoCollisionTest is the camera-collision feature: the arm
|
||||
// shortens when something is between the pivot and the desired camera position.
|
||||
ThirdPersonArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("ThirdPersonArm"));
|
||||
ThirdPersonArm->SetupAttachment(GetCapsuleComponent());
|
||||
ThirdPersonArm->SetRelativeLocation(FVector(0.0f, 0.0f, 60.0f));
|
||||
ThirdPersonArm->TargetArmLength = ThirdPersonBoomLength;
|
||||
ThirdPersonArm->bUsePawnControlRotation = true;
|
||||
ThirdPersonArm->bDoCollisionTest = true;
|
||||
ThirdPersonArm->ProbeSize = 12.0f;
|
||||
ThirdPersonArm->ProbeChannel = ECollisionChannel::ECC_Camera;
|
||||
ThirdPersonArm->bEnableCameraLag = true;
|
||||
ThirdPersonArm->CameraLagSpeed = 8.0f;
|
||||
ThirdPersonArm->SetAutoActivate(false);
|
||||
|
||||
ThirdPersonCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("ThirdPersonCamera"));
|
||||
ThirdPersonCamera->SetupAttachment(ThirdPersonArm, USpringArmComponent::SocketName);
|
||||
ThirdPersonCamera->bUsePawnControlRotation = false;
|
||||
ThirdPersonCamera->SetAutoActivate(false);
|
||||
ThirdPersonCamera->SetFieldOfView(90.0f);
|
||||
|
||||
// Hard-reference the Enhanced Input assets with ConstructorHelpers so the
|
||||
// cooker always includes them and they load during CDO construction. Runtime
|
||||
@@ -78,6 +133,10 @@ AGigLineCharacter::AGigLineCharacter()
|
||||
TEXT("/Game/Input/IA_Jump.IA_Jump"));
|
||||
static ConstructorHelpers::FObjectFinder<UInputAction> ResetF(
|
||||
TEXT("/Game/Input/IA_Reset.IA_Reset"));
|
||||
static ConstructorHelpers::FObjectFinder<UInputAction> DebugF(
|
||||
TEXT("/Game/Input/IA_ToggleDebug.IA_ToggleDebug"));
|
||||
static ConstructorHelpers::FObjectFinder<UInputAction> CamF(
|
||||
TEXT("/Game/Input/IA_ToggleCamera.IA_ToggleCamera"));
|
||||
static ConstructorHelpers::FObjectFinder<UInputAction> PauseF(
|
||||
TEXT("/Game/Input/IA_Pause.IA_Pause"));
|
||||
|
||||
@@ -89,6 +148,8 @@ AGigLineCharacter::AGigLineCharacter()
|
||||
LookAction = LookF.Object;
|
||||
JumpAction = JumpF.Object;
|
||||
ResetAction = ResetF.Object;
|
||||
ToggleCameraAction = CamF.Object;
|
||||
ToggleDebugAction = DebugF.Object;
|
||||
PauseAction = PauseF.Object;
|
||||
|
||||
if (!MappingContext || !MoveAction || !MoveBackAction || !StrafeAction || !StrafeLeftAction
|
||||
@@ -138,6 +199,23 @@ void AGigLineCharacter::BeginPlay()
|
||||
StartLocation = GetActorLocation();
|
||||
StartRotation = GetActorRotation();
|
||||
|
||||
// `-thirdperson` starts the pawn in the third-person rig. The first-person view
|
||||
// stays the default (owner decision for 0.1.0), but the screenshot run needs the
|
||||
// body visible without a human pressing V, so the review is reproducible.
|
||||
if (CVarGigLineSetupLogs.GetValueOnGameThread())
|
||||
{
|
||||
UE_LOG(LogTemp, Log,
|
||||
TEXT("GigLine: BeginPlay thirdperson=%d body=%s idle=%s walk=%s"),
|
||||
FParse::Param(FCommandLine::Get(), TEXT("thirdperson")) ? 1 : 0,
|
||||
GetMesh()->GetSkeletalMeshAsset() ? TEXT("ok") : TEXT("nil"),
|
||||
IdleAnimation ? TEXT("ok") : TEXT("nil"),
|
||||
WalkAnimation ? TEXT("ok") : TEXT("nil"));
|
||||
}
|
||||
if (FParse::Param(FCommandLine::Get(), TEXT("thirdperson")))
|
||||
{
|
||||
ToggleCamera();
|
||||
}
|
||||
|
||||
// The PlayerStart sits above grade so the capsule cannot spawn embedded, so
|
||||
// the BeginPlay position is mid-air. Re-capture once the pawn has landed,
|
||||
// otherwise Reset drops the player 1.4 m back into the air every press.
|
||||
@@ -178,6 +256,51 @@ void AGigLineCharacter::BeginPlay()
|
||||
{
|
||||
StartQASelfTest();
|
||||
}
|
||||
|
||||
// `-walk` holds forward input from the first frame. The screenshot run uses it
|
||||
// with `-thirdperson` so a capture can show the walk clip mid-stride: an idle
|
||||
// shot cannot prove the animation set, and a still cannot show a stride.
|
||||
bWalkForever = FParse::Param(FCommandLine::Get(), TEXT("walk"));
|
||||
|
||||
// `-perf` starts the overlay on: the performance capture and any manual frame
|
||||
// check should not depend on someone pressing F3 first. The built demo has
|
||||
// neither switch, so the counter stays hidden for players.
|
||||
if (FParse::Param(FCommandLine::Get(), TEXT("perf")) || FParse::Param(FCommandLine::Get(), TEXT("qaselftest")))
|
||||
{
|
||||
if (IConsoleVariable* Var = IConsoleManager::Get().FindConsoleVariable(TEXT("gigline.Debug")))
|
||||
{
|
||||
Var->Set(1, ECVF_SetByConsole);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AGigLineCharacter::ToggleDebugOverlay()
|
||||
{
|
||||
static IConsoleVariable* Var = IConsoleManager::Get().FindConsoleVariable(TEXT("gigline.Debug"));
|
||||
if (Var)
|
||||
{
|
||||
const bool bNowOn = Var->GetInt() == 0;
|
||||
Var->Set(bNowOn ? 1 : 0, ECVF_SetByConsole);
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine: debug overlay %s"), bNowOn ? TEXT("on") : TEXT("off"));
|
||||
}
|
||||
}
|
||||
|
||||
void AGigLineCharacter::SetReviewWalk(bool bEnable)
|
||||
{
|
||||
bWalkForever = bEnable;
|
||||
}
|
||||
|
||||
void AGigLineCharacter::ReturnToStartForReview()
|
||||
{
|
||||
ResetToStart();
|
||||
}
|
||||
|
||||
void AGigLineCharacter::WalkForever()
|
||||
{
|
||||
if (bWalkForever && GetCharacterMovement() && GetCharacterMovement()->IsMovingOnGround())
|
||||
{
|
||||
AddMovementInput(GetActorForwardVector(), 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void AGigLineCharacter::StartQASelfTest()
|
||||
@@ -379,7 +502,11 @@ void AGigLineCharacter::StartQASelfTest()
|
||||
// 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};
|
||||
// Sample the street envelope only: the carriageway plus the walkable
|
||||
// part of each sidewalk. Y 1000 used to be in this list and sits ~1 cm
|
||||
// from the frontage building line, so it grazed the theatre wall once
|
||||
// the kit's proud shopfronts went in - a probe artefact, not a blocker.
|
||||
static const float TraceYs[] = {-1400.0f, -167.0f, 800.0f, -1382.0f, 718.0f};
|
||||
for (float WallY : TraceYs)
|
||||
{
|
||||
for (float X0 = -19500.0f; X0 <= -3000.0f; X0 += 2500.0f)
|
||||
@@ -553,6 +680,14 @@ void AGigLineCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputCo
|
||||
{
|
||||
Input->BindAction(PauseAction, ETriggerEvent::Started, this, &AGigLineCharacter::TogglePause);
|
||||
}
|
||||
if (ToggleCameraAction)
|
||||
{
|
||||
Input->BindAction(ToggleCameraAction, ETriggerEvent::Started, this, &AGigLineCharacter::ToggleCamera);
|
||||
}
|
||||
if (ToggleDebugAction)
|
||||
{
|
||||
Input->BindAction(ToggleDebugAction, ETriggerEvent::Started, this, &AGigLineCharacter::ToggleDebugOverlay);
|
||||
}
|
||||
if (JumpAction)
|
||||
{
|
||||
Input->BindAction(JumpAction, ETriggerEvent::Started, this, &AGigLineCharacter::StartJump);
|
||||
@@ -640,6 +775,87 @@ void AGigLineCharacter::Look(const FInputActionValue& Value)
|
||||
AddControllerPitchInput(Axis.Y * LookSensitivity * (bInvertLookY ? -1.0f : 1.0f));
|
||||
}
|
||||
|
||||
void AGigLineCharacter::Tick(float DeltaSeconds)
|
||||
{
|
||||
Super::Tick(DeltaSeconds);
|
||||
// Speed-driven animation state: correct whether the player walked, was pushed,
|
||||
// or fell, and independent of which input produced the movement.
|
||||
WalkForever();
|
||||
UpdateBodyAnimation();
|
||||
|
||||
|
||||
}
|
||||
|
||||
void AGigLineCharacter::ToggleCamera()
|
||||
{
|
||||
// Both rigs exist from construction; the toggle only swaps which one is active
|
||||
// and whether the body is drawn. Deactivating the other camera matters: with
|
||||
// two active cameras the view is whichever was registered last.
|
||||
bThirdPerson = !bThirdPerson;
|
||||
FirstPersonCamera->SetActive(!bThirdPerson);
|
||||
ThirdPersonCamera->SetActive(bThirdPerson);
|
||||
GetMesh()->SetHiddenInGame(!bThirdPerson);
|
||||
if (bThirdPerson)
|
||||
{
|
||||
UpdateBodyAnimation();
|
||||
}
|
||||
if (CVarGigLineSetupLogs.GetValueOnGameThread())
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine: camera %s"), bThirdPerson ? TEXT("third person") : TEXT("first person"));
|
||||
}
|
||||
}
|
||||
|
||||
void AGigLineCharacter::UpdateBodyAnimation()
|
||||
{
|
||||
if (!bThirdPerson || !GetMesh())
|
||||
{
|
||||
return;
|
||||
}
|
||||
const bool bMoving = GetVelocity().Size2D() > WalkAnimationThreshold;
|
||||
UAnimSequence* Wanted = bMoving ? WalkAnimation.Get() : IdleAnimation.Get();
|
||||
if (!Wanted)
|
||||
{
|
||||
static bool bWarnedMissingClip = false;
|
||||
if (!bWarnedMissingClip)
|
||||
{
|
||||
bWarnedMissingClip = true;
|
||||
UE_LOG(LogTemp, Warning,
|
||||
TEXT("GigLine: body clip missing (idle=%s walk=%s) - body stays in bind pose"),
|
||||
IdleAnimation ? TEXT("ok") : TEXT("nil"),
|
||||
WalkAnimation ? TEXT("ok") : TEXT("nil"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (Wanted != PlayingAnimation || GetMesh()->GetSingleNodeInstance() == nullptr)
|
||||
{
|
||||
// Only the mode setter is idempotent; calling SetAnimation unconditionally
|
||||
// restarts the clip every frame, which freezes the body on frame 0 - that is
|
||||
// why the walking capture showed a still figure with both legs together.
|
||||
if (GetMesh()->GetAnimationMode() != EAnimationMode::AnimationSingleNode)
|
||||
{
|
||||
GetMesh()->SetAnimationMode(EAnimationMode::AnimationSingleNode);
|
||||
}
|
||||
if (Wanted != PlayingAnimation)
|
||||
{
|
||||
GetMesh()->SetAnimation(Wanted);
|
||||
PlayingAnimation = Wanted;
|
||||
GetMesh()->Play(true);
|
||||
if (CVarGigLineSetupLogs.GetValueOnGameThread())
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine: body clip %s, mode=%d"),
|
||||
*Wanted->GetName(), static_cast<int32>(GetMesh()->GetAnimationMode()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Same clip but the instance was recreated after a mode change: resume it
|
||||
// once rather than re-setting the animation.
|
||||
GetMesh()->Play(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AGigLineCharacter::ResetToStart()
|
||||
{
|
||||
// Reset is a level-restart in miniature: put the body back where it spawned,
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
#include "InputActionValue.h"
|
||||
#include "GigLineCharacter.generated.h"
|
||||
|
||||
class UAnimSequence;
|
||||
class UCameraComponent;
|
||||
class UInputAction;
|
||||
class UInputMappingContext;
|
||||
class USpringArmComponent;
|
||||
|
||||
|
||||
/**
|
||||
@@ -29,6 +31,7 @@ public:
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
|
||||
virtual void Tick(float DeltaSeconds) override;
|
||||
|
||||
protected:
|
||||
/** Loads the Enhanced Input assets from /Game/Input; called from BeginPlay. */
|
||||
@@ -38,6 +41,34 @@ protected:
|
||||
void ResetToStart();
|
||||
void TogglePause();
|
||||
|
||||
public:
|
||||
/** True while the third-person rig is the active view (for the debug overlay). */
|
||||
bool IsThirdPerson() const { return bThirdPerson; }
|
||||
|
||||
/** Review harness: hold or release forward input from the screenshot actor. */
|
||||
void SetReviewWalk(bool bEnable);
|
||||
|
||||
/** Review harness: put the pawn back where it spawned, after a walking capture. */
|
||||
void ReturnToStartForReview();
|
||||
|
||||
protected:
|
||||
/** Swaps between the first-person and third-person rigs (V). */
|
||||
void ToggleCamera();
|
||||
|
||||
/**
|
||||
* Plays the idle or walk clip on the visible body.
|
||||
*
|
||||
* Called from the movement tick rather than from input: an animation state
|
||||
* driven by speed is correct whether the player walked, was pushed, or fell.
|
||||
* Only switches when the clip changes, because PlayAnimation restarts a clip
|
||||
* and calling it every frame would freeze the cycle at its first key.
|
||||
*/
|
||||
void UpdateBodyAnimation();
|
||||
|
||||
/** Velocity above which the walk clip plays, in cm/s (0.1 m/s tolerance). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation")
|
||||
float WalkAnimationThreshold = 10.0f;
|
||||
|
||||
/**
|
||||
* TEMP QA: scripted walk / reset / pause verification.
|
||||
*
|
||||
@@ -109,6 +140,52 @@ protected:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
|
||||
TObjectPtr<UCameraComponent> FirstPersonCamera;
|
||||
|
||||
/**
|
||||
* Third-person boom. Collision testing is on, which is what stops the camera
|
||||
* from ending up inside a wall: the arm shortens until the camera is clear
|
||||
* instead of clipping through geometry.
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
|
||||
TObjectPtr<USpringArmComponent> ThirdPersonArm;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
|
||||
TObjectPtr<UCameraComponent> ThirdPersonCamera;
|
||||
|
||||
/** Boom length in cm: far enough to see the body, close enough to read the street. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera")
|
||||
float ThirdPersonBoomLength = 340.0f;
|
||||
|
||||
UPROPERTY()
|
||||
TObjectPtr<UAnimSequence> IdleAnimation;
|
||||
|
||||
UPROPERTY()
|
||||
TObjectPtr<UAnimSequence> WalkAnimation;
|
||||
|
||||
/** True while the third-person rig is the active view. */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Camera")
|
||||
bool bThirdPerson = false;
|
||||
|
||||
/** Clip currently playing on the body, so UpdateBodyAnimation can diff it. */
|
||||
UPROPERTY()
|
||||
TObjectPtr<UAnimSequence> PlayingAnimation;
|
||||
|
||||
/** True while `-walk` holds forward input for the screenshot run. */
|
||||
bool bWalkForever = false;
|
||||
|
||||
/** Applies the `-walk` forward input when the flag is set and the pawn is grounded. */
|
||||
void WalkForever();
|
||||
|
||||
/** Debug-overlay action (F3). */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputAction> ToggleDebugAction;
|
||||
|
||||
/** Flips the gigline.Debug console variable that the HUD reads. */
|
||||
void ToggleDebugOverlay();
|
||||
|
||||
/** Toggle-camera action (V). */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputAction> ToggleCameraAction;
|
||||
|
||||
/** Degrees per second per unit of mouse input; defaults to Unreal's usual 45. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera")
|
||||
float LookSensitivity = 45.0f;
|
||||
|
||||
@@ -2,11 +2,61 @@
|
||||
|
||||
#include "Engine/Canvas.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "GigLineCharacter.h"
|
||||
#include "HAL/IConsoleManager.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
|
||||
// Console variable so a packaged build can be checked without a rebuild:
|
||||
// gigline.Debug 1
|
||||
// The HUD reads it every frame; the F3 binding toggles it.
|
||||
static TAutoConsoleVariable<int32> CVarGigLineDebug(
|
||||
TEXT("gigline.Debug"),
|
||||
0,
|
||||
TEXT("Draw the performance and state overlay (0=off, 1=on). F3 toggles it."),
|
||||
ECVF_Cheat);
|
||||
|
||||
void AGigLineHUD::UpdateFrameStats(float DeltaSeconds)
|
||||
{
|
||||
if (DeltaSeconds <= 0.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
++FramesDrawn;
|
||||
const float Ms = DeltaSeconds * 1000.0f;
|
||||
if (WarmupSeconds > 0.0f)
|
||||
{
|
||||
WarmupSeconds -= DeltaSeconds;
|
||||
SmoothedFrameMs = FMath::Lerp(SmoothedFrameMs, Ms, 0.1f);
|
||||
WorstFrameMs = Ms;
|
||||
WorstWindowSeconds = 0.0f;
|
||||
return;
|
||||
}
|
||||
// A 0.9/0.1 EMA settles in a few frames and still reacts to a stall.
|
||||
SmoothedFrameMs = FMath::Lerp(SmoothedFrameMs, Ms, 0.1f);
|
||||
WorstFrameMs = FMath::Max(WorstFrameMs, Ms);
|
||||
// Rolling worst-frame window: a spike stays visible for two seconds, so a
|
||||
// hitch is not missed between glances (and not averaged away).
|
||||
WorstWindowSeconds += DeltaSeconds;
|
||||
if (WorstWindowSeconds >= 2.0f)
|
||||
{
|
||||
WorstWindowSeconds = 0.0f;
|
||||
WorstFrameMs = Ms;
|
||||
}
|
||||
}
|
||||
|
||||
void AGigLineHUD::DrawHUD()
|
||||
{
|
||||
Super::DrawHUD();
|
||||
|
||||
// Console variable is authoritative; the UPROPERTY is the per-instance default
|
||||
// for tests that set it before BeginPlay.
|
||||
if (IConsoleVariable* Var = IConsoleManager::Get().FindConsoleVariable(TEXT("gigline.Debug")))
|
||||
{
|
||||
bShowDebug = Var->GetInt() != 0;
|
||||
}
|
||||
UpdateFrameStats(GetWorld() ? GetWorld()->GetDeltaSeconds() : 0.0f);
|
||||
|
||||
if (!Canvas || AttributionLine.IsEmpty())
|
||||
{
|
||||
return;
|
||||
@@ -37,4 +87,35 @@ void AGigLineHUD::DrawHUD()
|
||||
DrawText(ControlsLine, Shadow, CX + 1.0f, CY + 1.0f, nullptr, TextScale * 0.82f);
|
||||
DrawText(ControlsLine, Ink, CX, CY, nullptr, TextScale * 0.82f);
|
||||
}
|
||||
|
||||
if (!bShowDebug)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Performance and state readout, top-left. Everything here is cheap enough to
|
||||
// leave on during a capture: four formatted lines and no additional passes.
|
||||
const APawn* Pawn = UGameplayStatics::GetPlayerPawn(this, 0);
|
||||
const AGigLineCharacter* Character = Cast<AGigLineCharacter>(Pawn);
|
||||
const float Fps = SmoothedFrameMs > 0.0f ? 1000.0f / SmoothedFrameMs : 0.0f;
|
||||
const FVector Where = Pawn ? Pawn->GetActorLocation() / 100.0f : FVector::ZeroVector;
|
||||
const float Speed = Pawn ? Pawn->GetVelocity().Size2D() / 100.0f : 0.0f;
|
||||
|
||||
const FString Header = FString::Printf(TEXT("%.0f FPS %.2f ms worst %.2f ms"), Fps, SmoothedFrameMs, WorstFrameMs);
|
||||
const FString State = FString::Printf(TEXT("%s %.2f m/s frames %lld"),
|
||||
Character && Character->IsThirdPerson() ? TEXT("third person") : TEXT("first person"),
|
||||
Speed, FramesDrawn);
|
||||
const FString Place = FString::Printf(TEXT("x %.1f y %.1f z %.1f m"), Where.X, Where.Y, Where.Z);
|
||||
const FString Hint = TEXT("F3 hide");
|
||||
|
||||
const FLinearColor DebugInk(0.85f, 1.0f, 0.55f, 0.95f);
|
||||
const FLinearColor DebugShadow(0.0f, 0.0f, 0.0f, 0.6f);
|
||||
const float DX = MarginPx;
|
||||
float DY = MarginPx;
|
||||
for (const FString& Line : { Header, State, Place, Hint })
|
||||
{
|
||||
DrawText(Line, DebugShadow, DX + 1.0f, DY + 1.0f, nullptr, TextScale * 0.9f);
|
||||
DrawText(Line, DebugInk, DX, DY, nullptr, TextScale * 0.9f);
|
||||
DY += 22.0f * TextScale * 0.9f;
|
||||
}
|
||||
}
|
||||
@@ -34,12 +34,25 @@ public:
|
||||
* in-game"). Keep in sync with Docs/controls.md.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Controls")
|
||||
FString ControlsLine = TEXT("WASD move \u00B7 Mouse look \u00B7 Space jump \u00B7 R reset \u00B7 Esc pause");
|
||||
FString ControlsLine = TEXT("WASD move \u00B7 Mouse look \u00B7 Space jump \u00B7 V view \u00B7 R reset \u00B7 Esc pause");
|
||||
|
||||
/** Licence line, including where the licence can be read. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attribution")
|
||||
FString LicenceLine = TEXT("Data \u00A9 OpenStreetMap contributors, ODbL 1.0 \u2014 openstreetmap.org/copyright");
|
||||
|
||||
/**
|
||||
* Performance and state readout, top-left, off by default.
|
||||
*
|
||||
* Toggle in game with **F3**, or start with it on by passing `-perf` (the
|
||||
* performance capture does). The built demo ships with it hidden so a player
|
||||
* does not see an FPS counter over Hollywood Boulevard.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Debug")
|
||||
bool bShowDebug = false;
|
||||
|
||||
/** Smoothed FPS and a rolling frame-time window, refreshed every frame. */
|
||||
void UpdateFrameStats(float DeltaSeconds);
|
||||
|
||||
/** Screen-space margin in pixels. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attribution")
|
||||
float MarginPx = 18.0f;
|
||||
@@ -47,4 +60,25 @@ public:
|
||||
/** Text scale relative to the default canvas font. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attribution")
|
||||
float TextScale = 1.45f;
|
||||
|
||||
private:
|
||||
/** Exponential moving average of frame time, in milliseconds. */
|
||||
float SmoothedFrameMs = 16.7f;
|
||||
|
||||
/** Worst frame in the rolling window, so a hitch is visible without a log. */
|
||||
float WorstFrameMs = 0.0f;
|
||||
|
||||
/** Seconds until the rolling worst-frame window resets. */
|
||||
float WorstWindowSeconds = 0.0f;
|
||||
|
||||
/** Frames drawn, so the overlay can show that the counter is live. */
|
||||
int64 FramesDrawn = 0;
|
||||
|
||||
/**
|
||||
* Seconds left in the warm-up window. The first frames of a session include
|
||||
* shader and streaming hitches of several hundred milliseconds; without this
|
||||
* the "worst" figure read 400 ms for the whole first two seconds and made a
|
||||
* healthy run look broken.
|
||||
*/
|
||||
float WarmupSeconds = 2.0f;
|
||||
};
|
||||
@@ -1,6 +1,10 @@
|
||||
#include "GigLineShotActor.h"
|
||||
|
||||
#include "GigLineCharacter.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
|
||||
#include "Camera/CameraActor.h"
|
||||
#include "Camera/CameraComponent.h"
|
||||
#include "Engine/World.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "UnrealClient.h"
|
||||
@@ -31,6 +35,12 @@ void AGigLineShotActor::Tick(float DeltaSeconds)
|
||||
|
||||
if (bWaitingForSettle)
|
||||
{
|
||||
// A follow shot tracks the pawn while it settles, otherwise the subject
|
||||
// walks out of the frame between placement and capture.
|
||||
if (CurrentShot < ShotFollow.Num() && ShotFollow[CurrentShot])
|
||||
{
|
||||
UpdateFollowCamera();
|
||||
}
|
||||
SettleCountdown -= DeltaSeconds;
|
||||
if (SettleCountdown <= 0.f)
|
||||
{
|
||||
@@ -52,11 +62,47 @@ void AGigLineShotActor::Tick(float DeltaSeconds)
|
||||
{
|
||||
bWaitingForScreenshot = false;
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLineShotActor: captured %s"), *FScreenshotRequest::GetFilename());
|
||||
// Walking shots move the pawn; put it back before the fixed-camera shots run.
|
||||
if (ShotFollow.IsValidIndex(CurrentShot) && ShotFollow[CurrentShot])
|
||||
{
|
||||
if (AGigLineCharacter* Character = Cast<AGigLineCharacter>(UGameplayStatics::GetPlayerPawn(this, 0)))
|
||||
{
|
||||
Character->ReturnToStartForReview();
|
||||
}
|
||||
}
|
||||
++CurrentShot;
|
||||
NextShot();
|
||||
}
|
||||
}
|
||||
|
||||
void AGigLineShotActor::UpdateFollowCamera()
|
||||
{
|
||||
if (!CurrentCamera)
|
||||
{
|
||||
return;
|
||||
}
|
||||
APawn* Pawn = UGameplayStatics::GetPlayerPawn(this, 0);
|
||||
if (!Pawn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Enable the walk here rather than only in NextShot: the sequencer's BeginPlay
|
||||
// runs before the player pawn exists, so a one-shot cast there silently found
|
||||
// nothing and the pawn never walked during its follow shot.
|
||||
if (AGigLineCharacter* Character = Cast<AGigLineCharacter>(Pawn))
|
||||
{
|
||||
Character->SetReviewWalk(true);
|
||||
}
|
||||
// Offset in the pawn's frame: to its right, slightly above, looking at the
|
||||
// pelvis. Right-of-pawn gives a profile view, which is where a stride reads.
|
||||
const FVector Right = Pawn->GetActorRightVector();
|
||||
const FVector Origin = Pawn->GetActorLocation();
|
||||
const FVector CameraLocation = Origin + Right * 420.f + FVector(0.f, 0.f, 90.f);
|
||||
const FVector Target = Origin + FVector(0.f, 0.f, 40.f);
|
||||
CurrentCamera->SetActorLocation(CameraLocation);
|
||||
CurrentCamera->SetActorRotation((Target - CameraLocation).Rotation());
|
||||
}
|
||||
|
||||
void AGigLineShotActor::NextShot()
|
||||
{
|
||||
if (!PlayerController)
|
||||
@@ -83,6 +129,29 @@ void AGigLineShotActor::NextShot()
|
||||
Params.ObjectFlags |= RF_Transient;
|
||||
CurrentCamera = GetWorld()->SpawnActor<ACameraActor>(
|
||||
ACameraActor::StaticClass(), ShotLocations[CurrentShot], ShotRotations[CurrentShot], Params);
|
||||
if (ShotFieldOfView.IsValidIndex(CurrentShot) && ShotFieldOfView[CurrentShot] > 0.0f)
|
||||
{
|
||||
if (UCameraComponent* Cam = CurrentCamera->GetCameraComponent())
|
||||
{
|
||||
// Long lens for elevations: fills the frame from across the street
|
||||
// without pitching the camera, so verticals stay parallel.
|
||||
Cam->SetFieldOfView(ShotFieldOfView[CurrentShot]);
|
||||
}
|
||||
}
|
||||
// Follow shots ignore their recorded transform after this point: the camera is
|
||||
// rebuilt from the pawn every frame until the capture. They also own the walking:
|
||||
// the pawn only holds forward input while a follow shot is being taken, so the
|
||||
// fixed-camera shots later in the list still find it at the spawn point.
|
||||
const bool bFollowThisShot = ShotFollow.IsValidIndex(CurrentShot) && ShotFollow[CurrentShot];
|
||||
if (AGigLineCharacter* Character = Cast<AGigLineCharacter>(UGameplayStatics::GetPlayerPawn(this, 0)))
|
||||
{
|
||||
Character->SetReviewWalk(bFollowThisShot);
|
||||
}
|
||||
if (bFollowThisShot)
|
||||
{
|
||||
UpdateFollowCamera();
|
||||
}
|
||||
|
||||
PlayerController->SetViewTarget(CurrentCamera);
|
||||
|
||||
SettleCountdown = SettleSeconds;
|
||||
|
||||
@@ -36,10 +36,32 @@ public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
TArray<FString> ShotNames;
|
||||
|
||||
/**
|
||||
* Optional per-shot field of view (degrees), same index as ShotLocations.
|
||||
* Zero or missing entries keep the engine default (90 deg). Elevation shots
|
||||
* taken from across the street need a long lens: a level camera 45 m out with
|
||||
* the default FOV leaves the building small in frame, and tilting it up to fill
|
||||
* the frame is what breaks the verticals.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
TArray<float> ShotFieldOfView;
|
||||
|
||||
/** Seconds to let the view target settle before the capture. Resolution comes from the runner's -ResX/-ResY. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
float SettleSeconds = 0.35f;
|
||||
|
||||
/**
|
||||
* Optional per-shot "follow the player" flag, same index as ShotLocations.
|
||||
*
|
||||
* A fixed camera cannot photograph a walking pawn: at 4.5 m/s it covers the
|
||||
* distance between two captures, so three attempts framed it at a different
|
||||
* distance each run and two missed it entirely. With this flag the camera is
|
||||
* re-placed every frame at a fixed offset in the pawn's frame, which is what
|
||||
* makes a stride shot deterministic.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
TArray<bool> ShotFollow;
|
||||
|
||||
/** Quit the game after the last shot (disable to keep the game open). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
bool bQuitWhenDone = true;
|
||||
@@ -47,6 +69,9 @@ public:
|
||||
private:
|
||||
void NextShot();
|
||||
|
||||
/** Re-places the active camera at the follow offset behind the player pawn. */
|
||||
void UpdateFollowCamera();
|
||||
|
||||
UPROPERTY()
|
||||
TObjectPtr<class APlayerController> PlayerController;
|
||||
|
||||
|
||||
@@ -239,289 +239,250 @@ Scope: facts and traps that cost time to establish. Planned work lives in [TODO.
|
||||
|
||||
## 2026-11-08 — reference photography gaps closed (local-only)
|
||||
|
||||
- Closed the two missing reference angles for the hero façade, both from Wikimedia
|
||||
Commons (gitignored deliverables under `Art/reference/`, provenance in sidecars):
|
||||
- `el-capitan-entrance/` — `File:Hollywood El Capitan entrance.jpg` (+ detail),
|
||||
Andreas Praefcke, CC BY 3.0. This is the isolated hero (6840) entrance/door
|
||||
recess that the notes doc had claimed Commons didn't hold. Query that found it:
|
||||
`--query "El Capitan entrance"`.
|
||||
- `el-capitan-night/` — `File:El Capitan theater with Tron marquee.jpg`
|
||||
(CC BY-SA 3.0) + West Side Story premiere (Adam Jones, CC BY 2.0). The theatre is
|
||||
the building NEXT to 6840; treat as proximity evidence only, not the hero.
|
||||
- Commons throttles hard: HTTP 429 after a batch. Fetch fewer files with
|
||||
`--delay`/separate calls and wait ~15-20s between runs (`queue.py` tool default
|
||||
1 req/s is insufficient once rate limited).
|
||||
- Task was claimed on branch `ref-photo-0.1.2`; delivers only the TODO closure (all
|
||||
actual reference files are gitignored, so no code branch content). Reported to
|
||||
coordinator for merge/cleanup.
|
||||
- Closed the two missing reference angles for the hero façade, both from Wikimedia Commons (gitignored deliverables under `Art/reference/`, provenance in sidecars):
|
||||
- `el-capitan-entrance/` — `File:Hollywood El Capitan entrance.jpg` (+ detail), Andreas Praefcke, CC BY 3.0. This is the isolated hero (6840) entrance/door recess that the notes doc had claimed Commons didn't hold. Query that found it: `--query "El Capitan entrance"`.
|
||||
- `el-capitan-night/` — `File:El Capitan theater with Tron marquee.jpg` (CC BY-SA 3.0) + West Side Story premiere (Adam Jones, CC BY 2.0). The theatre is the building NEXT to 6840; treat as proximity evidence only, not the hero.
|
||||
- Commons throttles hard: HTTP 429 after a batch. Fetch fewer files with `--delay`/separate calls and wait ~15-20s between runs (`queue.py` tool default 1 req/s is insufficient once rate limited).
|
||||
- Task was claimed on branch `ref-photo-0.1.2`; delivers only the TODO closure (all actual reference files are gitignored, so no code branch content). Reported to coordinator for merge/cleanup.
|
||||
|
||||
## 2026-11-08 — security-game scan: concepts copied, licensing ignored (owner)
|
||||
|
||||
- Scanned `/run/media/system/DATA/Documents/06-games/security-game` for reference
|
||||
material vs our `Art/reference/`. Result: its **photo set** (`docs/screens/refs/
|
||||
hollywood/`, 9 files) is already fully duplicated in our `Art/reference/hollywood/`
|
||||
(with SIDECAR/BOARD copies) — nothing new there.
|
||||
- What we lacked: its **AI concept renders** (`docs/screens/concepts/
|
||||
hollywood_boulevard/`, 12 PNGs + `.refgen.json`). Copied into
|
||||
`Art/reference/el-capitan-concepts/` with a folder README. **Owner explicitly
|
||||
said ignore licensing completely** — they are gitignored local development
|
||||
reference, never committed or shipped; `.refgen.json` still records the model
|
||||
and source photo per file for traceability.
|
||||
- Use concepts for mood/lighting intent only; geometry truth stays with the photo
|
||||
refs (facade, entrance, night marquee, TCL/Tussauds, Masonic 1922, etc.).
|
||||
- The security-game `realism/` A/B screenshots and `concepts/joke_shop/` are its
|
||||
own engine/venue renders — not relevant to our 6840 modeling.
|
||||
- Scanned `/run/media/system/DATA/Documents/06-games/security-game` for reference material vs our `Art/reference/`. Result: its **photo set** (`docs/screens/refs/ hollywood/`, 9 files) is already fully duplicated in our `Art/reference/hollywood/` (with SIDECAR/BOARD copies) — nothing new there.
|
||||
- What we lacked: its **AI concept renders** (`docs/screens/concepts/ hollywood_boulevard/`, 12 PNGs + `.refgen.json`). Copied into `Art/reference/el-capitan-concepts/` with a folder README. **Owner explicitly said ignore licensing completely** — they are gitignored local development reference, never committed or shipped; `.refgen.json` still records the model and source photo per file for traceability.
|
||||
- Use concepts for mood/lighting intent only; geometry truth stays with the photo refs (facade, entrance, night marquee, TCL/Tussauds, Masonic 1922, etc.).
|
||||
- The security-game `realism/` A/B screenshots and `concepts/joke_shop/` are its own engine/venue renders — not relevant to our 6840 modeling.
|
||||
|
||||
## 2026-11-08 — ODbL review closed; remaining active TODO items finished
|
||||
|
||||
- Wrote `Docs/odbl-review.md` (decision 2026-11-08): the shipped block geometry
|
||||
(streets/footprints/sidewalks) is a Produced Work of the OpenStreetMap database
|
||||
(ODbL 1.0). Obligations held: on-screen attribution via AGigLineHUD ("©
|
||||
OpenStreetMap contributors" + "Data © OSM, ODbL 1.0 — openstreetmap.org/copyright"),
|
||||
ODbL link, and the raw extract committed in Geo/ with a manifest (date, bbox,
|
||||
SHA-256). Share-alike is limited to the data layer; project code/art remain CC0
|
||||
(Docs/licensing.md decision). 3DEP DEM is US Government public domain, unrelated
|
||||
to ODbL.
|
||||
- Closed both [A] items in TODO: (1) sidewalks/roads/collision — collision
|
||||
verified; remaining kerb radii/aprons/kerb-height explicitly tracked as 0.1.1
|
||||
patch tasks (Sidewalk outer edge, Crossing ramp gradient, Kerb height);
|
||||
(2) ODbL review — recorded. The [D] Blosm-import (blockout cleaning) item stays
|
||||
deferred (tracked separately).
|
||||
- Wrote `Docs/odbl-review.md` (decision 2026-11-08): the shipped block geometry (streets/footprints/sidewalks) is a Produced Work of the OpenStreetMap database (ODbL 1.0). Obligations held: on-screen attribution via AGigLineHUD ("© OpenStreetMap contributors" + "Data © OSM, ODbL 1.0 — openstreetmap.org/copyright"), ODbL link, and the raw extract committed in Geo/ with a manifest (date, bbox, SHA-256). Share-alike is limited to the data layer; project code/art remain CC0 (Docs/licensing.md decision). 3DEP DEM is US Government public domain, unrelated to ODbL.
|
||||
- Closed both [A] items in TODO: (1) sidewalks/roads/collision — collision verified; remaining kerb radii/aprons/kerb-height explicitly tracked as 0.1.1 patch tasks (Sidewalk outer edge, Crossing ramp gradient, Kerb height); (2) ODbL review — recorded. The [D] Blosm-import (blockout cleaning) item stays deferred (tracked separately).
|
||||
- Branch: odbl-review-0.1.0 (docs + TODO closure only).
|
||||
|
||||
## 2026-11-08 — placement release check; north-buildings camera was 9.5 km off
|
||||
|
||||
- Release check 2 (placement vs references): rendered demo, probed DemoMap world
|
||||
positions, compared to Geo references. Matches: road 24.3 m (ref crossings
|
||||
20.9-25.9), sidewalk centreline offsets 12.5/13.2 m (ref 12.6/13.6), hero
|
||||
frontage 26.0 m / setback ~13 m (ref 25.75/14.5), six south-frontage buildings
|
||||
+ TCL/Tussaud's in verified OSM positions.
|
||||
- **Bug found:** the `demo-north-buildings` camera sat at X -22800 (the buildings'
|
||||
own X), aiming at its own position -> 85%-sky screenshot. Probed real geometry:
|
||||
TCL bbox X -22889..-18442, Y -9431..-1802; Tussaud's X -27071..-22799,
|
||||
Y -7586..-1888; hero at X -13336, Y +1307 (frontage 26 m). Camera now at
|
||||
(X -22750, Y +1600) looking north; sky fraction 85% -> 41%.
|
||||
- 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.
|
||||
- Release check 2 (placement vs references): rendered demo, probed DemoMap world positions, compared to Geo references. Matches: road 24.3 m (ref crossings 20.9-25.9), sidewalk centreline offsets 12.5/13.2 m (ref 12.6/13.6), hero frontage 26.0 m / setback ~13 m (ref 25.75/14.5), six south-frontage buildings
|
||||
- TCL/Tussaud's in verified OSM positions.
|
||||
- **Bug found:** the `demo-north-buildings` camera sat at X -22800 (the buildings' own X), aiming at its own position -> 85%-sky screenshot. Probed real geometry: TCL bbox X -22889..-18442, Y -9431..-1802; Tussaud's X -27071..-22799, Y -7586..-1888; hero at X -13336, Y +1307 (frontage 26 m). Camera now at (X -22750, Y +1600) looking north; sky fraction 85% -> 41%.
|
||||
- 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.
|
||||
- `-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.
|
||||
|
||||
## 2026-11-08 — setup logs gated behind a cvar (diagnostics item)
|
||||
|
||||
- The remaining spawn/input setup logs (settle-timer "start captured", "GigLine
|
||||
IN:" pushed/bound/errors) are now gated behind `gigline.SetupLogs`
|
||||
(TAutoConsoleVariable, default **0** via `DefaultEngine.ini [ConsoleVariables]`).
|
||||
Re-enable at runtime: `gigline.SetupLogs 1`.
|
||||
- The `-qaselftest` QA logs stay ungated (deliberate release-check feature per
|
||||
Docs/release-checks.md). Verified packaged build: 0 setup-log lines, QA suite
|
||||
still all PASS (walk/reset/pause/transect 0 holes/wall 0-35).
|
||||
- Branch qa-diagnostics-0.1.0. SIM/GROUND probes were removed earlier in the
|
||||
DefaultKeyMappings fix (PR #8).
|
||||
- The remaining spawn/input setup logs (settle-timer "start captured", "GigLine IN:" pushed/bound/errors) are now gated behind `gigline.SetupLogs` (TAutoConsoleVariable, default **0** via `DefaultEngine.ini [ConsoleVariables]`). Re-enable at runtime: `gigline.SetupLogs 1`.
|
||||
- The `-qaselftest` QA logs stay ungated (deliberate release-check feature per Docs/release-checks.md). Verified packaged build: 0 setup-log lines, QA suite still all PASS (walk/reset/pause/transect 0 holes/wall 0-35).
|
||||
- Branch qa-diagnostics-0.1.0. SIM/GROUND probes were removed earlier in the DefaultKeyMappings fix (PR #8).
|
||||
|
||||
## 2026-11-08 — v0.1.0 released
|
||||
|
||||
- All six `0.1.0` release checks pass and are recorded in `Docs/release-checks.md`:
|
||||
traversal (seam transect 0 holes), placement vs references, collision+packaging
|
||||
(wall probe 0/35), 60 fps at 1440p (2.42 ms avg; TSR on, VSync off, no FG),
|
||||
fresh-checkout build, packaged Linux build with controls shown in-game.
|
||||
- `CHANGELOG.md` gets the `0.1.0` entry; the completed `0.1.0` section was removed
|
||||
from `TODO.md` (goal stays in `ROADMAP.md`); tag `v0.1.0` cut on `main`.
|
||||
- All six `0.1.0` release checks pass and are recorded in `Docs/release-checks.md`: traversal (seam transect 0 holes), placement vs references, collision+packaging (wall probe 0/35), 60 fps at 1440p (2.42 ms avg; TSR on, VSync off, no FG), fresh-checkout build, packaged Linux build with controls shown in-game.
|
||||
- `CHANGELOG.md` gets the `0.1.0` entry; the completed `0.1.0` section was removed from `TODO.md` (goal stays in `ROADMAP.md`); tag `v0.1.0` cut on `main`.
|
||||
- Owner approved the regenerated screenshot set (PR #15) on 2026-11-08.
|
||||
- THIRD_PARTY_NOTICES switched to checkbox statuses (`[x]`/`[P]`/`[ ]`); all tools
|
||||
incl. Blosm, QGIS and Git LFS are now verified as tool-only.
|
||||
- Next work: the `0.1.1` collision-detail patch (sidewalk outer edge 15.8 cm step,
|
||||
crossing ramp gradient 10 %, kerb height assumption, traversal regression).
|
||||
- THIRD_PARTY_NOTICES switched to checkbox statuses (`[x]`/`[P]`/`[ ]`); all tools incl. Blosm, QGIS and Git LFS are now verified as tool-only.
|
||||
- Next work: the `0.1.1` collision-detail patch (sidewalk outer edge 15.8 cm step, crossing ramp gradient 10 %, kerb height assumption, traversal regression).
|
||||
|
||||
## 2026-11-08 — v0.1.1 released (collision patch)
|
||||
|
||||
- Sidewalk upstand now tapers full-at-kerb → zero at the building edge (removes the
|
||||
15.8 cm outer ledge). Crossing ramp run derives from `--max-ramp-grade` (8 % ⇒
|
||||
1.63 m) instead of a fixed 1.5 m/8.7 %. Street + site rebuilt, site re-imported,
|
||||
DemoMap rebuilt, packaged build verified.
|
||||
- Pre-check tool fixes: coverage no longer shrinks with `--sample` (it used
|
||||
`range(int(block_len))`), and it reports grade + location of the worst step.
|
||||
- Residual, documented not hidden: 13.4 cm surface step at s≈198 (south) / 8.0 cm
|
||||
at s≈251 (north) — a strip discontinuity near a centreline vertex, below the
|
||||
45 cm MaxStepHeight, so not blocking.
|
||||
- Kerb height finalised at 0.15 m modelled / 0.13 m built (6 in LA standard; DEM
|
||||
and photos can't resolve finer).
|
||||
- Sidewalk upstand now tapers full-at-kerb → zero at the building edge (removes the 15.8 cm outer ledge). Crossing ramp run derives from `--max-ramp-grade` (8 % ⇒ 1.63 m) instead of a fixed 1.5 m/8.7 %. Street + site rebuilt, site re-imported, DemoMap rebuilt, packaged build verified.
|
||||
- Pre-check tool fixes: coverage no longer shrinks with `--sample` (it used `range(int(block_len))`), and it reports grade + location of the worst step.
|
||||
- Residual, documented not hidden: 13.4 cm surface step at s≈198 (south) / 8.0 cm at s≈251 (north) — a strip discontinuity near a centreline vertex, below the 45 cm MaxStepHeight, so not blocking.
|
||||
- Kerb height finalised at 0.15 m modelled / 0.13 m built (6 in LA standard; DEM and photos can't resolve finer).
|
||||
|
||||
## 2026-11-09 — Mapbox building data as a second height source
|
||||
|
||||
- `Geo/tools/mapbox_buildings.py` (new, stdlib `urllib`) Tilequeries
|
||||
`mapbox.mapbox-streets-v8` layer `building` at every building centroid in the
|
||||
extract and merges the answer with our OSM height + source. Token comes from
|
||||
`MAPBOX_ACCESS_TOKEN` in the ignored `.env`; responses cache to
|
||||
`Geo/raw/mapbox-cache/`.
|
||||
- **Mapbox terms restrict persisting/redistributing their data**, so `.gitignore`
|
||||
now covers `/Geo/raw/mapbox-cache/` and `/Geo/verification/*mapbox*`. The tool
|
||||
also refuses to cache a response that contains the token.
|
||||
- `Geo/tools/mapbox_buildings.py` (new, stdlib `urllib`) Tilequeries `mapbox.mapbox-streets-v8` layer `building` at every building centroid in the extract and merges the answer with our OSM height + source. Token comes from `MAPBOX_ACCESS_TOKEN` in the ignored `.env`; responses cache to `Geo/raw/mapbox-cache/`.
|
||||
- **Mapbox terms restrict persisting/redistributing their data**, so `.gitignore` now covers `/Geo/raw/mapbox-cache/` and `/Geo/verification/*mapbox*`. The tool also refuses to cache a response that contains the token.
|
||||
- **First run (72 buildings): 58 match, 5 close, 3 disagree, 6 non-building.**
|
||||
- It independently classifies our roof/canopy/shelter volumes as `roof`/
|
||||
`shelter` — corroborating the `0.1.2` finding that they are not buildings.
|
||||
- Remaining disagreements are untyped `building=yes`/`apartments` rear
|
||||
structures where our fallback guess (2 × 3.2 m = 6.4 m) meets Mapbox's 3.0 m.
|
||||
- Every "close" case is a `building:levels × 3.2 m` building where Mapbox is
|
||||
**0.6 m lower per 6 levels** → Mapbox implies **3.1 m per level**, not 3.2.
|
||||
Worth revisiting `LEVEL_HEIGHT` if a measured storey height ever lands.
|
||||
- It independently classifies our roof/canopy/shelter volumes as `roof`/ `shelter` — corroborating the `0.1.2` finding that they are not buildings.
|
||||
- Remaining disagreements are untyped `building=yes`/`apartments` rear structures where our fallback guess (2 × 3.2 m = 6.4 m) meets Mapbox's 3.0 m.
|
||||
- Every "close" case is a `building:levels × 3.2 m` building where Mapbox is **0.6 m lower per 6 levels** → Mapbox implies **3.1 m per level**, not 3.2. Worth revisiting `LEVEL_HEIGHT` if a measured storey height ever lands.
|
||||
- Its query API surface: `height`, `min_height`, `type`, `extrude` per polygon.
|
||||
|
||||
## 2026-11-09 — Mapbox calibration adopted into the blockout
|
||||
|
||||
- Owner decision: adopt Mapbox. Applied:
|
||||
- `LEVEL_HEIGHT` 3.2 → **3.1 m** in `Geo/tools/block_height_report.py`, and the
|
||||
Blosm `--level-height` default in `Art/blender/blockout/blosm_blockout.py`.
|
||||
- `HEIGHT_OVERRIDES` in block_height_report: ways 1490910971, 1492058875,
|
||||
1492747608 → 3.0 m (Mapbox single-storey; the 2-level guess gave 6.4 m).
|
||||
- `classify_height(tags, level_height, way_id)` now takes the way id so
|
||||
overrides apply; `mapbox_buildings.py` passes it, so the cross-check agrees
|
||||
with the height table.
|
||||
- `LEVEL_HEIGHT` 3.2 → **3.1 m** in `Geo/tools/block_height_report.py`, and the Blosm `--level-height` default in `Art/blender/blockout/blosm_blockout.py`.
|
||||
- `HEIGHT_OVERRIDES` in block_height_report: ways 1490910971, 1492058875, 1492747608 → 3.0 m (Mapbox single-storey; the 2-level guess gave 6.4 m).
|
||||
- `classify_height(tags, level_height, way_id)` now takes the way id so overrides apply; `mapbox_buildings.py` passes it, so the cross-check agrees with the height table.
|
||||
- Re-running the cross-check: **66 match, 6 non-building, 0 close/disagree**.
|
||||
- Geometry: blockout regenerated at 3.1 (object names preserved — the frontage
|
||||
exporter maps `element.020`/`element`/`element.019`/named buildings, all still
|
||||
present, 6/6 exported), frontage re-imported, DemoMap rebuilt, packaged build
|
||||
`-qaselftest` all PASS.
|
||||
- **Blosm adds roof mass** on top of `levels × levelHeight`: 6922 39.1 m vs table
|
||||
37.2, El Capitan Theatre 20.5 m vs 18.6 — both ~+1.9 m. That is a roof, not a
|
||||
height error; the table is the tagged height, the mesh is storey + roof.
|
||||
- Gotcha: regenerating the blockout is safe for names, but any future change to
|
||||
the import must re-verify the 6/6 frontage export and the `element.*` mapping.
|
||||
- Geometry: blockout regenerated at 3.1 (object names preserved — the frontage exporter maps `element.020`/`element`/`element.019`/named buildings, all still present, 6/6 exported), frontage re-imported, DemoMap rebuilt, packaged build `-qaselftest` all PASS.
|
||||
- **Blosm adds roof mass** on top of `levels × levelHeight`: 6922 39.1 m vs table 37.2, El Capitan Theatre 20.5 m vs 18.6 — both ~+1.9 m. That is a roof, not a height error; the table is the tagged height, the mesh is storey + roof.
|
||||
- Gotcha: regenerating the blockout is safe for names, but any future change to the import must re-verify the 6/6 frontage export and the `element.*` mapping.
|
||||
|
||||
## 2026-11-09 — hero colonnade proportion fixed (47.6 % → 53.1 %)
|
||||
|
||||
- The hero's colonnade (columns 2.00–7.95 + capitals 7.95–8.90) was 6.90 m of the
|
||||
14.50 m elevation = **47.6 %**, below the photographed 50–60 %, because the
|
||||
entablature (3.40 m) and parapet (2.20 m) used 5.60 m of the height.
|
||||
- Thinned ENTABLATURE_H 3.40 → 3.00 and PARAPET_H 2.20 → 1.80 in
|
||||
`Tools/hero_facade_build.py`; the colonnade grows to **7.70 m = 53.1 %** while the
|
||||
envelope, motto band and medallions keep their relationships (all positioned
|
||||
relative to those two constants).
|
||||
- **Gotcha (cost a bad build):** `hero_facade_build.py` takes `--out` for the
|
||||
**FBX** and `--save-blend` for the `.blend`. Passing `--out <file>.blend` writes
|
||||
FBX bytes over the blend and Blender then reports "File format is not supported".
|
||||
Correct invocation: `--out /tmp/gigline/hero-facade.fbx --save-blend Art/...blend
|
||||
--report Art/...json`.
|
||||
- Rebuilt hero → re-imported (`SM_HeroFacade`, bounds 2604 × 1257 × 1450 cm) →
|
||||
DemoMap rebuilt → screenshots re-rendered. Note `ue_hero_facade_import.py` saves
|
||||
the map, so `chmod u+w` the `.umap` first or the import silently does not persist.
|
||||
- The hero's colonnade (columns 2.00–7.95 + capitals 7.95–8.90) was 6.90 m of the 14.50 m elevation = **47.6 %**, below the photographed 50–60 %, because the entablature (3.40 m) and parapet (2.20 m) used 5.60 m of the height.
|
||||
- Thinned ENTABLATURE_H 3.40 → 3.00 and PARAPET_H 2.20 → 1.80 in `Tools/hero_facade_build.py`; the colonnade grows to **7.70 m = 53.1 %** while the envelope, motto band and medallions keep their relationships (all positioned relative to those two constants).
|
||||
- **Gotcha (cost a bad build):** `hero_facade_build.py` takes `--out` for the **FBX** and `--save-blend` for the `.blend`. Passing `--out <file>.blend` writes FBX bytes over the blend and Blender then reports "File format is not supported". Correct invocation: `--out /tmp/gigline/hero-facade.fbx --save-blend Art/...blend --report Art/...json`.
|
||||
- Rebuilt hero → re-imported (`SM_HeroFacade`, bounds 2604 × 1257 × 1450 cm) → DemoMap rebuilt → screenshots re-rendered. Note `ue_hero_facade_import.py` saves the map, so `chmod u+w` the `.umap` first or the import silently does not persist.
|
||||
|
||||
## 2026-11-09 — AI detail plates from reference photos; floating windows fixed
|
||||
|
||||
- `Tools/facade_detail_concepts.py` generates architectural detail plates from one
|
||||
local reference photograph via OpenRouter image models (default
|
||||
`google/gemini-2.5-flash-image`; the sibling project uses the same route in
|
||||
`tools/generate_textures.py` — chat/completions with a text + image_url content
|
||||
part, image returned as `message.images[].image_url.url`). Eight studies,
|
||||
`--list` free, `--all` generates, `-vN` never overwrites, `.refgen.json` sidecar
|
||||
per image, all output under gitignored `Art/reference/facade-details/`.
|
||||
- **Prompt gotcha:** the model refuses when the prompt describes features the
|
||||
reference photograph does not show ("the image you sent appears to be a
|
||||
decorative architectural detail, not an entrance with steps"). Prompts must
|
||||
describe the *reference subject* and only restage light/people/surface.
|
||||
- `Tools/facade_detail_concepts.py` generates architectural detail plates from one local reference photograph via OpenRouter image models (default `google/gemini-2.5-flash-image`; the sibling project uses the same route in `tools/generate_textures.py` — chat/completions with a text + image_url content part, image returned as `message.images[].image_url.url`). Eight studies, `--list` free, `--all` generates, `-vN` never overwrites, `.refgen.json` sidecar per image, all output under gitignored `Art/reference/facade-details/`.
|
||||
- **Prompt gotcha:** the model refuses when the prompt describes features the reference photograph does not show ("the image you sent appears to be a decorative architectural detail, not an entrance with steps"). Prompts must describe the _reference subject_ and only restage light/people/surface.
|
||||
- **Output is real:** 1184×864 to 1344×768 PNGs, ~2 MB each.
|
||||
- **Measured defect found and fixed:** hero window panes sat at Y −1.98 while the
|
||||
mass front is Y −0.50 and the columns reach −1.02 — i.e. glazing floating 1.44 m
|
||||
in front of the wall and ahead of the colonnade. Now `WINDOW_Y = −0.56`
|
||||
(6 cm proud of the wall, behind the columns). Visible as dark openings in
|
||||
`review-hero-facade-front`.
|
||||
- Reminder: `.blend` is LFS read-only on checkout — `chmod u+w` or Blender fails
|
||||
with "Cannot save blend file … not writable" (this bit again here).
|
||||
- **Measured defect found and fixed:** hero window panes sat at Y −1.98 while the mass front is Y −0.50 and the columns reach −1.02 — i.e. glazing floating 1.44 m in front of the wall and ahead of the colonnade. Now `WINDOW_Y = −0.56` (6 cm proud of the wall, behind the columns). Visible as dark openings in `review-hero-facade-front`.
|
||||
- Reminder: `.blend` is LFS read-only on checkout — `chmod u+w` or Blender fails with "Cannot save blend file … not writable" (this bit again here).
|
||||
|
||||
## 2026-11-09 — UE export hygiene: smoothing groups + facade collision
|
||||
|
||||
- **All our FBX exporters used `mesh_smooth_type="OFF"`**, which is why every Unreal
|
||||
import logged "No smoothing group information was found for this mesh". The hero
|
||||
now exports `"FACE"` and its round parts are shaded with
|
||||
`shade_smooth_by_angle(35°)` before export: cylinders read as round instead of
|
||||
faceted, and the import warning is gone (6 → 0).
|
||||
- **The hero façade had no simple collision** (complex-only), so line traces and
|
||||
capsule sweeps passed straight through it — the player could walk through the
|
||||
wall of the demo. Caught by a new `-qaselftest` **facade occlusion probe** (40 m
|
||||
sideways traces from the road centre at walking height).
|
||||
- **Auto-generated convex collision on a 95-part 26 m façade is unusable**: it
|
||||
produced coarse hulls that bulged into the street and the wall probe went
|
||||
0/35 → 2/35. Rejected in favour of `UseComplexAsSimple` via
|
||||
`UGigLineAssetTools.set_complex_collision_as_simple` (same reason the terrain
|
||||
uses it), saved on the asset.
|
||||
- Probe expectations must mirror the staged buildings: the facade probe now carries
|
||||
explicit X spans per frontage (and the TCL/Tussaud's spans on the north side) so
|
||||
a gap between buildings is not reported as a defect.
|
||||
- **All our FBX exporters used `mesh_smooth_type="OFF"`**, which is why every Unreal import logged "No smoothing group information was found for this mesh". The hero now exports `"FACE"` and its round parts are shaded with `shade_smooth_by_angle(35°)` before export: cylinders read as round instead of faceted, and the import warning is gone (6 → 0).
|
||||
- **The hero façade had no simple collision** (complex-only), so line traces and capsule sweeps passed straight through it — the player could walk through the wall of the demo. Caught by a new `-qaselftest` **facade occlusion probe** (40 m sideways traces from the road centre at walking height).
|
||||
- **Auto-generated convex collision on a 95-part 26 m façade is unusable**: it produced coarse hulls that bulged into the street and the wall probe went 0/35 → 2/35. Rejected in favour of `UseComplexAsSimple` via `UGigLineAssetTools.set_complex_collision_as_simple` (same reason the terrain uses it), saved on the asset.
|
||||
- Probe expectations must mirror the staged buildings: the facade probe now carries explicit X spans per frontage (and the TCL/Tussaud's spans on the north side) so a gap between buildings is not reported as a defect.
|
||||
|
||||
## 2026-11-09 — one FBX export preset instead of six copies
|
||||
|
||||
- The `bpy.ops.export_scene.fbx(...)` argument block was duplicated in six scripts
|
||||
(hero, block frontage, north buildings, site, scale test, MPFB character), and
|
||||
all six carried `mesh_smooth_type="OFF"` — hence the "No smoothing group
|
||||
information was found" warning on every import.
|
||||
- Now `Tools/fbx_export_preset.py` holds `ue_metric_v1` and `ue_character_v1`:
|
||||
`preset_kwargs()` (bpy-free, unit-tested) plus `export_static()` /
|
||||
`export_character()` wrappers that import `bpy` lazily. Every caller imports it
|
||||
and passes `selection=True/False`; the seven duplicated blocks are gone.
|
||||
- Callers in `Tools/` resolve the module with
|
||||
`sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))`; the Blender-side
|
||||
`Art/blender/site/export_site_fbx.py` computes the repo root
|
||||
(`..`, `..`, `..`) and joins `Tools`. Getting that wrong silently inserts
|
||||
`Tools/Tools` — check the import path when adding a caller.
|
||||
- Verified end to end: scale test, hero, frontage (6/6), site (7/7) and north
|
||||
buildings all re-exported through the preset, re-imported with **0 smoothing
|
||||
warnings**, DemoMap rebuilt and `-qaselftest` all PASS (walk, transect 0 holes,
|
||||
wall probe 0/35, facade probe 0/9).
|
||||
- `mpfb_character_export.py` was not re-run: it needs the MPFB add-on on
|
||||
`BLENDER_USER_SCRIPTS` ("Add-on not loaded: bl_ext.blender_org.mpfb"), which is
|
||||
pre-existing and unrelated; the character is not in `0.1.x` gameplay.
|
||||
- The `bpy.ops.export_scene.fbx(...)` argument block was duplicated in six scripts (hero, block frontage, north buildings, site, scale test, MPFB character), and all six carried `mesh_smooth_type="OFF"` — hence the "No smoothing group information was found" warning on every import.
|
||||
- Now `Tools/fbx_export_preset.py` holds `ue_metric_v1` and `ue_character_v1`: `preset_kwargs()` (bpy-free, unit-tested) plus `export_static()` / `export_character()` wrappers that import `bpy` lazily. Every caller imports it and passes `selection=True/False`; the seven duplicated blocks are gone.
|
||||
- Callers in `Tools/` resolve the module with `sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))`; the Blender-side `Art/blender/site/export_site_fbx.py` computes the repo root (`..`, `..`, `..`) and joins `Tools`. Getting that wrong silently inserts `Tools/Tools` — check the import path when adding a caller.
|
||||
- Verified end to end: scale test, hero, frontage (6/6), site (7/7) and north buildings all re-exported through the preset, re-imported with **0 smoothing warnings**, DemoMap rebuilt and `-qaselftest` all PASS (walk, transect 0 holes, wall probe 0/35, facade probe 0/9).
|
||||
- `mpfb_character_export.py` was not re-run: it needs the MPFB add-on on `BLENDER_USER_SCRIPTS` ("Add-on not loaded: bl_ext.blender_org.mpfb"), which is pre-existing and unrelated; the character is not in `0.1.x` gameplay.
|
||||
|
||||
## 2026-11-09 — v0.1.2 released
|
||||
|
||||
- Cornice finished: projecting corona over a **52-dentil course** (0.18 × 0.30 ×
|
||||
0.22 m at 0.45 m centres, z 12.12–12.34, 0.25 m clear of the corona). Hero is now
|
||||
3 044 tris across 148 parts, still 14.50 m. Verified: packaged `-qaselftest` all
|
||||
PASS (transect 0 holes, wall probe 0/35, facade probe 0/9).
|
||||
- `CHANGELOG.md` gets the `0.1.2` entry (added/changed/fixed/verified/limitations),
|
||||
the `0.1.2` section is removed from `TODO.md`, `ROADMAP.md` status updated, tag
|
||||
`v0.1.2` cut after the merge.
|
||||
- Assumptions recorded rather than hidden: dentil spacing, base and abacus
|
||||
dimensions are not resolvable in our reference photography.
|
||||
- Cornice finished: projecting corona over a **52-dentil course** (0.18 × 0.30 × 0.22 m at 0.45 m centres, z 12.12–12.34, 0.25 m clear of the corona). Hero is now 3 044 tris across 148 parts, still 14.50 m. Verified: packaged `-qaselftest` all PASS (transect 0 holes, wall probe 0/35, facade probe 0/9).
|
||||
- `CHANGELOG.md` gets the `0.1.2` entry (added/changed/fixed/verified/limitations), the `0.1.2` section is removed from `TODO.md`, `ROADMAP.md` status updated, tag `v0.1.2` cut after the merge.
|
||||
- Assumptions recorded rather than hidden: dentil spacing, base and abacus dimensions are not resolvable in our reference photography.
|
||||
|
||||
## 2026-11-09 — elevation cameras levelled (straight verticals)
|
||||
|
||||
- archviz MUST: an elevation must not lean. `hero-facade-front` looked up ~4° from
|
||||
4.2 m at a 7.6 m target; `demo-spawn-view` and `demo-street-level` were pitched
|
||||
too. All three now put the camera at the target's height, so
|
||||
`FindLookAtRotation` yields pitch 0 and roll 0 and verticals are parallel by
|
||||
construction.
|
||||
- Do not try to *measure* verticality from the rendered PNG with a naive edge
|
||||
scan: banners, windows and the parapet produce different edge sets at different
|
||||
heights and the pairwise match is meaningless (it reported a 418 px "drift").
|
||||
Verify it geometrically in the camera list instead.
|
||||
- archviz MUST: an elevation must not lean. `hero-facade-front` looked up ~4° from 4.2 m at a 7.6 m target; `demo-spawn-view` and `demo-street-level` were pitched too. All three now put the camera at the target's height, so `FindLookAtRotation` yields pitch 0 and roll 0 and verticals are parallel by construction.
|
||||
- Do not try to _measure_ verticality from the rendered PNG with a naive edge scan: banners, windows and the parapet produce different edge sets at different heights and the pairwise match is meaningless (it reported a 418 px "drift"). Verify it geometrically in the camera list instead.
|
||||
|
||||
## 2026-11-09 — 0.1.3 packaging/setup fixes
|
||||
|
||||
- **`Tools/ue_common.py`**: `make_writable()` clears the read-only bit an LFS
|
||||
`.umap` checks out with (otherwise `save_map()` returns False with no error and
|
||||
the staged actors vanish), and `require_saved()` makes that failure visible.
|
||||
`ue_hero_facade_import.py` and `ue_import_north_buildings.py` never did this —
|
||||
which is why importing the hero appeared to succeed while saving nothing.
|
||||
Verified by forcing `chmod u-w` on DemoMap first: the run logged
|
||||
`map writable: chmod u+w … (was 0o444)`, then `save_map: True`, exit 0.
|
||||
- Gotcha while adding it: `ue_hero_facade_import.py` had **no** `import os`/`sys`
|
||||
(only `unreal`), so the sys.path line raised `NameError: name 'sys' is not
|
||||
defined` inside the commandlet — an exit code of 255 with no obvious cause.
|
||||
- Docs: `build.md` gained the current pipeline table (7 site FBXs, frontage, north,
|
||||
hero, map), the `Builds/GigLineQA` archive, the packaged-log path, `-qaselftest`
|
||||
usage and a **traps** table; `setup.md` marks the fresh-checkout build verified and
|
||||
documents the two optional API keys; architecture 07 + combined doc corrected the
|
||||
build-output path.
|
||||
- **`Tools/ue_common.py`**: `make_writable()` clears the read-only bit an LFS `.umap` checks out with (otherwise `save_map()` returns False with no error and the staged actors vanish), and `require_saved()` makes that failure visible. `ue_hero_facade_import.py` and `ue_import_north_buildings.py` never did this — which is why importing the hero appeared to succeed while saving nothing. Verified by forcing `chmod u-w` on DemoMap first: the run logged `map writable: chmod u+w … (was 0o444)`, then `save_map: True`, exit 0.
|
||||
- Gotcha while adding it: `ue_hero_facade_import.py` had **no** `import os`/`sys` (only `unreal`), so the sys.path line raised `NameError: name 'sys' is not defined` inside the commandlet — an exit code of 255 with no obvious cause.
|
||||
- Docs: `build.md` gained the current pipeline table (7 site FBXs, frontage, north, hero, map), the `Builds/GigLineQA` archive, the packaged-log path, `-qaselftest` usage and a **traps** table; `setup.md` marks the fresh-checkout build verified and documents the two optional API keys; architecture 07 + combined doc corrected the build-output path.
|
||||
|
||||
## 2026-11-09 — 0.1.4 materials + architecture-camera pass
|
||||
|
||||
- **Material graphs**: `ensure_material()` now supports masonry coursing (`courses_m`) and roughness variation. **UE's `WorldPosition` is in CENTIMETRES**, so a course height in metres must be ×100 — the first attempt divided by 0.55 and produced coursing 100× too fine to see.
|
||||
- **`connect_material_expressions` returns False instead of raising**, so an unknown pin name leaves an input unconnected and the material silently compiles with a default (a mask of 0 is invisible). All links now go through a `link()` helper that logs failures. There is also **no `MaterialExpressionLess`**; use `Frac → Divide → OneMinus → Clamp` for a soft threshold band, or `MaterialExpressionStep`/`SmoothStep` if a hard one is wanted.
|
||||
- **Material assignment was slot-0-only**, which on the hero is the _banner_ — the stone, shadowed stone and glazing kept their flat Blender materials. Assignment is now per slot, keyed on the imported slot material's name (`glass`/`banner`/`stoneshadow`/`stone`/`roof`/`wall`). Hero: 4/4 slots.
|
||||
- New materials: `M_FacadeStoneShadow` (same coursing, a stop darker), `M_FacadeGlass` (roughness 0.14, specular 0.62 ≈ IOR 1.5 expressed without a refraction pass), `M_FacadeBanner` (the one saturated surface).
|
||||
- **Camera FOV**: levelling the elevation cameras left the building small in frame (39 % sky). Added optional per-shot `ShotFieldOfView` to `AGigLineShotActor` and a 4th element on the shot tuples; a long lens (42–55°) fills the frame from across the street while the camera stays level, so verticals stay parallel. `review-hero-facade-front` sky 39 → 20 %, `demo-spawn-view` 23 → 16 %.
|
||||
|
||||
## 2026-11-09 — facade texture maps (0.1.4)
|
||||
|
||||
- `Tools/generate_facade_textures.py` writes deterministic 512² base-colour and roughness maps (ashlar stone, stucco, asphalt, concrete) with a pure-zlib PNG encoder and hash-based value noise — no numpy/Pillow, ~14 s for all eight, and byte-identical on a re-run (checked by md5sum).
|
||||
- `ue_build_demo_map.py` imports them to `/Game/Hollywood/Textures` and samples them **world-aligned**: WorldPosition (cm) → ×(1/tile_cm) → ComponentMask(R,G) → TextureSample UVs. `tile_cm` per material: stone 440, stucco 260, asphalt 900, concrete 250.
|
||||
- Gotchas: `MaterialExpressionTextureSample`'s UV input pin is **"UVs"**, and the output pin for colour is **"RGB"** / roughness **"R"**. `ComponentMask` pins are R/G/B/A booleans. A textured material skips the graph coursing on purpose — overlaying both would double the course pattern.
|
||||
- The script needs `ROOT` in `ue_build_demo_map.py` and `sys` imported; forgetting either fails with `NameError` and an exit code of 0 from the commandlet (always read the result file under /tmp/gigline).
|
||||
|
||||
## 2026-11-09 — façade module kit (0.1.4)
|
||||
|
||||
- `Tools/facade_kit_build.py` builds the kit at the measured **4.96 m bay** and **3.10 m storey**: pilaster, sill, lintel, window bay, shopfront bay, door bay, cornice, parapet + coping = 16 meshes, exported per-object with `fbx_export_preset` plus a JSON part report (dims + evidence per piece).
|
||||
- `Tools/ue_import_facade_kit.py` → `/Game/Hollywood/Kit/*`, complex-as-simple collision (a convex hull would fence off the shopfront glazing).
|
||||
- `Tools/ue_apply_facade_kit.py` lays the kit out from each building's own bbox: `bays = round(width / 496 cm)`, storeys from height minus the 310 cm ground floor. **638 pieces** over six frontage buildings + both north buildings.
|
||||
- **Bug worth remembering:** the first window piece was modelled entirely at +Y (into the wall), so every window sat _inside_ the solid blockout wall and was invisible in every render while the placement maths looked right. The kit now projects the surround 3 cm proud with the glazing 6 cm behind its face — a solid-wall blockout cannot carry a real recess, so the piece has to project.
|
||||
- Blender gotcha: clearing the scene _including materials_ invalidates handles already taken ("StructRNA of type Material has been removed"); clear objects and orphan meshes only, and create the material after the clear.
|
||||
- The kit meshes are spawned at the world origin with their position baked into the mesh (like the frontage exports), so placement uses the bbox directly and must not add the actor location.
|
||||
|
||||
## 2026-11-09 — CRITICAL: blockout must be regenerated with --origin
|
||||
|
||||
- Regenerating the blockout in 0.1.2 (for the 3.1 m storey height) was run **without `--origin`**, so Blosm centred on the extract instead of fitting the project frame. The whole frontage shifted about **119 m in X and 2.4 m in Y**, and the hero ended up outside the 6902/6838 gap — the "verified placement" was undone silently, and every render still looked like a plausible block.
|
||||
- Correct invocation (README documents it, and the affine fit prints `frame correction residual: max 0.0 cm`): `blosm_blockout.py --osm … --bounds 34.1006,-118.3422,34.1020,-118.3370 --level-height 3.1 --origin 34.101545,-118.338718 --assets-dir … --out …`
|
||||
- After re-export + re-import, the frontage spans are back to the verified values: 6922 −24276..−17700, 6902 −17749..−14591, **hero −14644..−12028 between them**, theatre −12082..−8210, and the north buildings at −22889..−18442 / −27071..−22799.
|
||||
- **`ue_import_north_buildings.py` also lacked `import sys`**, so its sys.path line raised NameError and the import silently did nothing (exit 255). Same trap as the hero importer: these scripts imported only `unreal`.
|
||||
- Lesson: after any blockout regeneration, probe the staged buildings' world X spans and compare them with `Geo/verification/2026-11-06-el-capitan-block.md` before trusting a render. A shift of this size is invisible in a screenshot.
|
||||
|
||||
## 2026-11-09 — kit naming + topology validation (blender-modeler checklist)
|
||||
|
||||
- Kit assets now follow the Unreal convention: the Blender object keeps its readable name (`Kit_Pilaster`), the exported asset and file are `SM_Kit_Pilaster`. The kit loader strips the `SM_` prefix so the layout code still addresses pieces by short name.
|
||||
- `facade_kit_build.py::validate()` runs the blender-modeler cleanup checklist on every piece before handoff — non-manifold edges, loose vertices, triangle count — and records the result in the part report. Current kit: 16 pieces, **all manifold, no loose geometry, 12 tris each**.
|
||||
- `ue_import_facade_kit.py` now deletes kit assets that the current build no longer produces, so a rename cannot leave two generations of the same piece in the folder (the rename from `Kit_*` to `SM_Kit_*` did exactly that until it did).
|
||||
|
||||
## 2026-11-09 — visual comparison loop; screenshots are demo-only
|
||||
|
||||
- `Tools/visual_loop.py` closes the reference → render → feedback loop. `--verify` gates captures against `Tools/visual_targets.json` (blank, tone-less, wrong-size fail); `--compare` measures luma, RMS contrast, edge density, palette distance and a 3x3 luma grid against the reference and prints a 0–1 score; `--iteration N` writes `Art/reference/iterations/N/{metrics.json,feedback.md}`. The feedback names the skill to consult, so the next modelling pass starts from a measurement rather than an opinion.
|
||||
- **Iterations are gitignored** (`/Art/reference/iterations/`); only confirmed shots are promoted into `Screenshots/`. A test asserts the ignore rule and that every target is a demo-map shot.
|
||||
- `Screenshots/` now holds **demo-map captures only** — the review staging map is no longer rendered for review. `render_screenshots.sh --verify` renders the demo map, collects the shots and runs the verification gate in one step.
|
||||
- **PIL gotchas:** `FIND_EDGES` leaves its own border ring at 128 (236/3600 px on a 60x60 frame = 6.5% "detail" on a perfectly flat image), so crop **after** filtering — cropping before does not help, the new border gets the same treatment. Palette shares must divide by the full-image area, not the cropped one.
|
||||
- First comparison (iteration 2): spawn view 0.70, street level 0.35, crossing 0.41, north buildings 0.34. Edge density is the dominant gap everywhere (0.04–0.11 against 0.20–0.32 in the references) — the render still lacks the relief the photographs carry, which is exactly what the `0.1.4` façade work is for.
|
||||
|
||||
## 2026-11-09 — theatre marquee + blade sign
|
||||
|
||||
- `Tools/theatre_trim_build.py` builds the El Capitan Theatre's signature features in **world coordinates**: marquee canopy 12.0 × 2.60 m at a 4.60 m soffit, illuminated fascia, recessed soffit, vertical blade sign 1.30 × 5.20 m with frame and bracket. `Tools/ue_stage_theatre_trim.py` imports to `/Game/Hollywood/Theatre` and stages one actor per piece (single spawn at the origin — no placement arithmetic).
|
||||
- **Two placement bugs, both invisible in a render:**
|
||||
1. **Axis sign.** Blender +Y is Unreal −Y, so a face at Unreal Y +1001 is Blender Y −10.01. Using +10.01 put the whole marquee on the far side of the boulevard.
|
||||
2. **Absolute Z.** Trim heights were absolute while the pavement at the theatre is 4.56 m above the project datum, so the canopy hung 5 cm above the pavement — the QA wall probe caught it as a knee-height blocker. `GROUND_Z` now offsets every height.
|
||||
- Wall probe: a scan row at Y 1000 sat ~1 cm from the frontage building line and grazed the theatre wall once the kit's proud shopfronts went in. Rows now sample the street envelope only (−1400, −167, 800, −1382, 718).
|
||||
|
||||
## 2026-11-09 — 0.1.4 signed off; 0.1.5 created for the carried geometry
|
||||
|
||||
- Owner reviewed the four demo captures locally and approved the set (7/7 items at the time). The signoff lived in `Docs/reviews/0.1.4-review.md`, which was removed on 2026-11-09; the per-shot `Approved` column of `Screenshots/README.md` is now the only record and is authoritative.
|
||||
- The three geometry items that `0.1.4` did not finish — retail neighbours from their own references, hero fluting/motto band, blockout cleaning — move to a new **`0.1.5` Façade finishing** patch rather than growing this one, plus a much-needed **1440p performance re-measure** (625 kit actors have never been measured; `0.1.0` measured 414 fps).
|
||||
- Decision recorded because it sets a precedent: finish and sign off a version when its _stated_ scope is done, and carry leftover scope into the next patch — do not let a patch accumulate unmeasured geometry.
|
||||
|
||||
## 2026-11-09 — quality priority flipped: realism first, performance last
|
||||
|
||||
- Owner decision: realism leads; performance closes the quality list. Applied in `Docs/Architecture/01_introduction_and_goals.md`, `10_quality_requirements.md`, `04_solution_strategy.md`, the combined `architecture.md`, `Docs/release-checks.md` (performance section now last, with the reason), `ROADMAP.md` (a "Quality priority" section) and `AGENTS.md` (the tie-break rule next to the scope-cutting rule).
|
||||
- Ordering used: 1 visual realism, 2 geographic fidelity, 3 walkability/usability, 4 build reproducibility/portability, 5 provenance, 6 performance.
|
||||
- **Both revisions carry the same guard so the order cannot be used as an excuse:** performance is a _floor_, not a preference — 60 FPS at 1440p without frame generation must be met at every release, and if realism cannot fit, the realism scope is cut deliberately and recorded in `CHANGELOG.md`, never the frame rate. Walkability outranks realism in one direction: a block the player falls through is not a demo.
|
||||
|
||||
## 2026-11-09 — 0.1.5 start: retail reference + kit layout maths
|
||||
|
||||
- Commons holds exactly **one** usable reference for the retail run: `Art/reference/retail-neighbours/01-lee-drug-and-neighbor.jpg` (Gb321, CC BY-SA 4.0, 1920×953) — 6800 Lee Drug **and** its neighbour at street level, shopfronts and parapets visible (vision pre-check: yes). Nothing for 6822 or 6806–6820, so those two façades still have no reference and stay blocked.
|
||||
- **Kit layout was wrong for tall/wide buildings**, not just ugly: bays were `round(width / 496)`, so 6922 (65.8 m wide, 37.1 m tall) got **13 bays** — a window every 4.96 m across the whole frontage — and the storey count came out at 11 × 310 cm = 34.1 m against a 35.7 m window zone, leaving bare wall under the cornice. Fixed: `MAX_BAYS = 9` (module widens instead of subdividing), and the storey spread is derived from the available height (`usable / storeys`) so the top band always lands inside the building. Result: 6922 9 × 11, theatre 8 × 6, 6806 7 × 3, 6800 4 × 3, TCL 9 × 5.
|
||||
|
||||
## 2026-11-09 — hero fluting + motto band; and a silent placement divergence
|
||||
|
||||
- Fluting: 20 grooves per column as thin shadow-tone cylinders set into the shaft (120 pieces) rather than boolean cuts — the eye reads them in silhouette and grazing light, booleans would cost far more for that. Motto band is now a real recess: surround 6 cm proud, panel 12 cm behind (was an 8 cm dark stripe). Hero 148 parts/3 044 tris -> 271/5 492, still 14.50 m.
|
||||
- **The real find:** `ue_hero_facade_import.py` had its own hero position `(-12024.5, -1450, 0)` — the pre-`0.1.0` value, before the frontage placement fix moved the hero to `(-13336, +1307)`. Running the importer on its own therefore teleported the hero **13 m east and onto the north side of the boulevard**, and nothing complained: a displaced building still renders. The only symptom was the reference score dropping 0.69 -> 0.56 with the 3x3 composition correlation flipping +0.54 -> -0.08.
|
||||
- Fix: `Tools/ue_placement.py` is now the single source of truth (hero position, verified frontage spans, `span_error`/`describe_span_failure`). Both `ue_hero_facade_import.py` and `ue_build_demo_map.py` import it, the import asserts the staged span and logs `PLACEMENT ERROR` when it does not match, and tests assert the stale literal cannot come back. Spawn-view score back to **0.72** (best yet).
|
||||
- Lesson to keep: **duplicated placement constants drift silently.** The metric that caught it was the composition correlation, not a visual review.
|
||||
|
||||
## 2026-11-09 — 1440p re-measured with the kit: 363 fps, ~12% cost
|
||||
|
||||
- 2560×1440, Epic preset, TSR, VSync off, no frame generation: **2.76 ms average (363 fps)**, p50 2.73, p95 3.17, p99 3.64, max 4.62 — against `0.1.0`'s 2.42 ms (414 fps). The 625 kit actors + 8 generated textures cost about **12 %** and leave ~6× the 60 fps floor.
|
||||
- **Variance trap confirmed a third time:** runs reporting ~105 fps (9.5 ms) were two overlapping game instances — an earlier launch had not exited. `pgrep -af Binaries/Linux/GigLine` should show one wrapper + one binary; 363 fps reproduced twice on a clean run. Always count instances before believing a perf number.
|
||||
- Series committed at `Docs/perf/0.1.5-kit-walk-frametimes.csv`; comparison table in `Docs/release-checks.md`.
|
||||
|
||||
## 2026-11-09 — 604 unmaterialed actors: material rebuild was delete+recreate
|
||||
|
||||
- The "blockout cleaning pass" turned out to need **material and placement hygiene, not more geometry**. `Tools/ue_audit_scene.py` (new) found **604 of 620 mesh actors with no material**: kit pieces and theatre trim all untextured.
|
||||
- **Cause:** `ensure_material` in `ue_build_demo_map.py` deleted and recreated each `/Game/Hollywood/Site/M_*` asset every run ("so a look change cannot be masked by a stale asset"). That orphane every reference: actors staged by other tools pointed at deleted assets and fell back to the default material. The map looked only _slightly_ flat — no error, no warning.
|
||||
- **Fix:** update the material **in place** — load it, `MaterialEditingLibrary. delete_all_material_expressions(mat)`, rebuild the graph, recompile, save. Note `Material.expressions` is protected from Python; use the library's deleter.
|
||||
- **Ordering rule now documented and enforced:** the demo builder rebuilds materials, so kit and theatre staging must run _after_ it. `render_screenshots.sh` now runs demo map → kit assembly → theatre trim → scene audit → shot cameras → capture, and **fails on a non-zero audit count**.
|
||||
- Second find: 53 duplicated kit labels (both shop piers of a bay shared one name).
|
||||
- Scene audit after the fixes: **0 failures** (all materialed, all spans verified, no duplicate labels). Packaged QA still PASS; spawn-view reference score 0.72.
|
||||
|
||||
## 2026-11-09 — v0.1.5 released
|
||||
|
||||
- All actionable `0.1.5` work is done: hero fluting + recessed motto band, kit layout for wide/tall buildings, placement single-source-of-truth, 1440p re-measure (363 fps), and the material/placement hygiene that the "blockout cleaning" task actually needed (604 unmaterialed actors, 53 duplicate labels) with a new scene audit gate.
|
||||
- Only item carried forward is the **retail frontage (6822 / 6806–6820 / 6800)**, blocked because Commons holds no usable reference for two of the three buildings and substituting the theatre's photographs would misattribute the façades. It now lives in a `0.1.6` Retail frontage patch.
|
||||
- Precedent applied again: tag when the stated scope is done, carry blocked scope into the next patch rather than letting a release wait indefinitely on missing reference photography.
|
||||
|
||||
## 2026-11-09 — Retail frontage and two pipeline traps
|
||||
|
||||
- **`Tools/render_screenshots.sh` called `./Engine/...` for the capture step** while every commandlet above went through `run_cmdlet`'s `cd "$ENGINE"`. With `set -e` the script died at exit 127 _after_ rebuilding the maps, so the PNGs silently stayed a day old and `--verify` never ran. Fixed to `"$ENGINE/Engine/Binaries/..."`. Symptom to remember: maps rebuild, screenshots do not move.
|
||||
- **The generic façade kit is applied to every actor labelled `Frontage_*` or `North_*`**, so staging a hand-modelled façade under those labels gets the kit pasted over it — two contradicting façade languages on one wall. `ue_apply_facade_kit.py` now skips `MODELLED` labels, and retail trim stages as `RetailTrim_*`.
|
||||
- **A Blender builder cannot be imported by a unit test** (`import bpy` at module scope). Pure layout arithmetic lives in `Tools/_common.py`; `Tools/tests/test_retail_frontage.py` tests it there, and the builder delegates. Same trick `fbx_export_preset.py` uses for its preset dicts.
|
||||
- **Joining 85 parts into one mesh per building** before export cut the Unreal import from 85 assets to 2, and the staging step to two spawns. Record per-part dimensions _before_ the join: after it only the joined bounds exist.
|
||||
|
||||
## 2026-11-09 — Third-person body renders in bind pose (open defect)
|
||||
|
||||
- **Symptom**: the pawn's body is visible and skinned, the run logs `GigLine: playing body clip Character_AnimHuman_rig_GigLine_Idle`, and the figure still stands in the A-pose bind. Two captures were taken before the call was proven to succeed — the camera, then the material, were blamed first and neither was the cause.
|
||||
- **The FBX is not the problem.** Re-importing `/tmp/gigline/character-anim.fbx` into Blender gives two actions, `GigLine_Idle` (frames 1–60) and `GigLine_Walk` (1–30), each with **1379 f-curves over 137 bones**. Animation is baked and exported correctly, so the defect is on the Unreal side.
|
||||
- **The material was a real but separate defect**: `M_Character_Export` had no `SkeletalMesh` usage flag, so Unreal substituted the default grey with only a `LogMaterial` warning. Fixed in place (`used_with_skeletal_mesh = True`).
|
||||
- **`PlayAnimation` alone is not enough.** `USkeletalMeshComponent::PlayAnimation` needs the component already in `AnimationSingleNode` mode; the body is hidden at construction (`SetHiddenInGame(true)`) and only unhidden by the camera toggle, so the first call can land on a component that is not yet in the right mode and never retries — the guard `Wanted == PlayingAnimation` then returns forever and the pose stays at bind.
|
||||
- **What to log next time**: animation mode and `GetSingleNodeInstance()` together with the clip name. A clip name alone shows the intent, not the effect.
|
||||
- **Fixed 2026-11-09, two independent causes.** (1) `PlayAnimation` needs `AnimationSingleNode` plus a live single-node instance; the body is hidden at construction and only unhidden by the camera toggle, so the first call could land before the component was ready and the `Wanted == PlayingAnimation` guard then returned forever. `UpdateBodyAnimation` now sets the mode, calls `SetAnimation` + `Play`, and re-asserts while `GetSingleNodeInstance()` is null. (2) The arms-down offsets lived only in the clip keys — the exporter writes clip data **relative to the rest pose**, so they arrived in Unreal as a ±20° swing around an A-posed bind. `apply_base_pose` now bakes them into the rest pose before any clip is authored, which is what `mpfb_character_export.py --pose idle` already did.
|
||||
- **The base pose needs inverted signs when baked to rest.** Applied as written, the FBX axis mapping produced arms raised in a V; negating the table gives arms down. Both states were verified on the imported asset, not assumed.
|
||||
- **Unreal bone names use underscores**: `upperarm01_L`, not Blender's `upperarm01.L`. `GetBoneIndex(TEXT("upperarm01.L"))` returns INDEX_NONE, `GetBoneTransform` then returns identity, and a probe reading it reports a constant 0.0000 — which looks like "animation not applied" and is actually "bone not found". Check `AnimPose.get_bone_names()` before trusting any bone probe.
|
||||
- **`Log` verbosity does not reach the capture log, `Warning` does.** Two diagnostic runs produced zero lines because the messages were `UE_LOG(LogTemp, Log, ...)`; the same lines at `Warning` appeared. Instrument with Warning when the evidence has to survive into `/tmp/gigline/render-*.log`.
|
||||
|
||||
## 2026-11-09 — Unreal silently refuses to replace an imported asset
|
||||
|
||||
- **The real reason the character pose never changed across a dozen rebuilds:** the FBX importer reported success every time, but `Character_Anim` and its two AnimSequences kept their original data. Proof: after changing the arm base from 1.55 rad to 0.55 rad and rebuilding, `AnimPose.get_bone_pose("upperarm01_L")` returned the **identical** quaternion `(+0.006, +0.002, -0.179, -0.984)` as before. An asset that does not change when its source does is not being written.
|
||||
- `AssetImportTask.replace_existing = True` was set, and `EditorAssetLibrary.delete_asset` returned **False** for both clips, even after `chmod u+w` (the `.uasset` files are LFS-lockable and check out read-only). Deleting is blocked because the pawn's C++ constructor hard-references the clips.
|
||||
- **Workaround that works: import under a new name.** `Tools/ue_character_anim_import.py` now uses `PREFIX = "CharacterV2"`, which creates `CharacterV2`, `CharacterV2_Skeleton` and `CharacterV2Human_rig_GigLine_{Idle,Walk}` fresh; the same quaternion probe then returned `(+0.439, +0.898)`, i.e. the 0.55 rad arm rotation had finally landed.
|
||||
- **Check the asset, not the importer.** A pose or material change that does not alter the probed asset data did not happen, whatever the log says. Probing a clip's bone quaternion takes one commandlet and settles it in seconds.
|
||||
- **The arms-down rotation belongs in the clips, not in the bind.** Every bind experiment (bake pose to rest, clear shape keys, apply the armature modifier, re-bind) was either impossible on this shape-keyed MPFB mesh or left Unreal showing the old bind. `Tools/character_anim_build.py` now carries a measured base offset in the clip keys (`BASE`: 0.55 rad on `upperarm01`, chosen by sweeping the rotation in Blender and reading the bone direction), and the character stands with its arms at its sides.
|
||||
@@ -108,8 +108,9 @@ Solo-development estimate: **approximately 30–40 weeks**, subject to asset qua
|
||||
| `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 |
|
||||
| `0.9.0` | 31–34 | Realistic buildings: façade system, per-building models, measured material library, LODs and collision proxies | `0.9.1` façade fixes |
|
||||
| `0.10.0` | 35–38 | Feature freeze, public beta, license and build audit | `0.10.1` blocker fixes; `0.10.2` regression fixes; `0.10.3` documentation fixes |
|
||||
| `1.0.0` | 39–44 | 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.
|
||||
|
||||
|
||||
Loaded 100 of 150 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user