Compare commits
27
Commits
6b4e1252c5
...
v0.1.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e2c5e29c0 | ||
|
|
d6d3d54810 | ||
|
|
ba68dcfdbc | ||
|
|
640bff9631 | ||
|
|
5ffe62f6aa | ||
|
|
81f7cd3991 | ||
|
|
68d8745534 | ||
|
|
420f863999 | ||
|
|
6a778dbc63 | ||
|
|
e75e5500a7 | ||
|
|
0dd4a55c87 | ||
|
|
88b0149bfa | ||
|
|
47bf3a2415 | ||
|
|
3a1db78473 | ||
|
|
b27d30d556 | ||
|
|
4bc41e9faf | ||
|
|
31f073d608 | ||
|
|
71acffec12 | ||
|
|
82a408bc33 | ||
|
|
7e9573a735 | ||
|
|
95011d678a | ||
|
|
abdd7d181f | ||
|
|
16c2bf7c05 | ||
|
|
a3ce9be38a | ||
|
|
2f87002711 | ||
|
|
37a3bfbc3d | ||
|
|
4832e66c00 |
No files matched your search
@@ -42,6 +42,15 @@ Thumbs.db
|
||||
.env.*
|
||||
*.env
|
||||
|
||||
# Mapbox-derived data — local only. Mapbox terms restrict persisting/redistributing
|
||||
# their data, so the Tilequery cache and any generated report stay untracked.
|
||||
/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
|
||||
|
||||
@@ -110,6 +110,59 @@ Keep entries short and dated, with the evidence: the command, the log line, or t
|
||||
|
||||
Memory edits on an agent's branch are not shared live. Communicate important findings to the coordinator during work and reconcile them into the shared `main` copy when integrating.
|
||||
|
||||
## Python and shell tooling
|
||||
|
||||
Every committed asset and every measured claim in this repository is produced by a script. Tools live in `Tools/` (Unreal, network, repository), `Geo/tools/` (projection, OSM/DEM parsing, metadata) and `Art/blender/*/` (Blender-side builders, next to their output).
|
||||
|
||||
**Fix the tool, not the artefact.** If an asset is corrected by hand, script the correction (or regenerate it) in the same change. A second manual fix of the same kind means the tool is missing a step.
|
||||
|
||||
**Improve over fork.** Extend the existing tool rather than adding a near-duplicate. When two or three scripts start sharing logic, factor the shared part into `Tools/_common.py`, `Geo/tools/osm_common.py` or `Art/blender/common.py`; keep the single metres→centimetres conversion where it already lives.
|
||||
|
||||
Rules that keep tools reusable and reviewable:
|
||||
|
||||
- **One script, one job.** Every input is an argument; no interactive prompts; deterministic output paths.
|
||||
- **Fail loudly.** A tool that cannot do its job exits non-zero with a message. Never leave a partially written asset and a success code.
|
||||
- **Result files for Unreal.** Unreal commandlets swallow `print()`; write the outcome to `/tmp/gigline/<tool>.txt` and read that instead of trusting the exit code alone.
|
||||
- **Absolute paths into sandboxes.** Blender Flatpak (`LD_LIBRARY_PATH="$B/blender/lib"`) and Unreal do not share the repo's working directory; pass absolute paths.
|
||||
- **Clean worktree for generators.** Tools that rewrite committed assets (site/street/hero builders, importers, map builders) run from a clean worktree; inspect the LFS diff before committing. Note that `.blend` and `.umap` check out read-only — `chmod u+w` first or the save silently fails.
|
||||
- **Network and paid-API tools are human-triggered.** `Tools/commons_fetch.py`, `Tools/page_image_fetch.py`, `Tools/judge_screenshot.py` (OpenRouter) and anything similar never sit in a gate or CI. Handle HTTP 429 with back-off rather than retrying immediately.
|
||||
- **Measure, don't assert.** Verification tools print the sampled numbers — counts, holes, Z ranges, worst step and its location, frame-time percentiles — so a claim in `TODO.md`, `CHANGELOG.md` or `Docs/` can be re-checked by re-running the command.
|
||||
|
||||
**Document every tool.** A new or changed tool gets a row in the inventory table of the README beside it: [`Tools/README.md`](Tools/README.md), [`Geo/README.md`](Geo/README.md), or [`Art/blender/README.md`](Art/blender/README.md). What it does, its arguments, and a typical command. A tool without a README row is unfinished.
|
||||
|
||||
**Test the pure parts.** `Tools/tests/` holds stdlib `unittest` tests (no Blender or Unreal process; they must not start a game or editor). Extract the testable core — argument parsing, geometry maths, licence gating, report formatting — into functions that a test can call directly.
|
||||
|
||||
```bash
|
||||
python3 -m unittest discover -s Tools/tests -t Tools -p 'test_*.py'
|
||||
```
|
||||
|
||||
Keep the tools standard-library-only where practical; if a third-party import is genuinely needed, add it to `requirements-tools.txt` with a version floor and mention it in the tool's README row.
|
||||
|
||||
## Architecture documentation
|
||||
|
||||
[`Docs/Architecture/`](Docs/Architecture/) is the arc42 documentation: twelve numbered section files, `about-arc42.md`, and the combined [`architecture.md`](Docs/Architecture/architecture.md). **Markdown is canonical** — the `.adoc` sources were removed on 2026-11-08.
|
||||
|
||||
Keep it current **in the same change as the implementation**, not in a later documentation pass. When work alters any of the following, edit the matching section:
|
||||
|
||||
| Change | Section |
|
||||
| --- | --- |
|
||||
| A new or restructured module, tool, data source or external interface | `05_building_block_view.md` |
|
||||
| A runtime flow, tick/order dependency, or lifecycle (possession, map load, QA self-test) | `06_runtime_view.md` |
|
||||
| Packaging, engine pin, checkout/build/launch procedure | `07_deployment_view.md` |
|
||||
| A pattern, convention or cross-cutting rule (units, collision policy, provenance) | `08_concepts.md` |
|
||||
| A decision that constrains future work | new ADR in `09_architecture_decisions.md` |
|
||||
| A quality goal or a measured budget (frame time, traversal coverage) | `10_quality_requirements.md` |
|
||||
| A known risk or a risk that retired | `11_technical_risks.md` |
|
||||
| A new term, tool or abbreviation | `12_glossary.md` |
|
||||
|
||||
Rules:
|
||||
|
||||
- **Describe what exists.** No aspirational architecture: anything planned belongs in `ROADMAP.md` and `TODO.md`. Every structural claim names the file, script or flag that implements it.
|
||||
- **ADRs are append-only.** Never rewrite an accepted decision; supersede it with a new ADR that says what it replaces and why.
|
||||
- **Keep the combined document in sync.** `architecture.md` mirrors the section files; when a section changes, update the combined document too.
|
||||
- **Images** live in `Docs/Architecture/images/` and are referenced, not duplicated.
|
||||
- **Verify:** all Markdown must pass `prettier --check` (config in `.prettierrc`), and a command you quote must still run.
|
||||
|
||||
## Geographic & Art Pipeline
|
||||
|
||||
1. Save a versioned OSM extract; document date, bounds, and source.
|
||||
@@ -147,8 +200,6 @@ Always use a consistent scale test, named export presets, and repeatable import
|
||||
- Record asset provenance and redistribution permissions in `THIRD_PARTY_NOTICES.md`.
|
||||
- Verify licenses for every MPFB asset, texture, animation, vehicle, and audio file.
|
||||
- An asset whose license cannot be verified must not be committed. Record it as unresolved in `THIRD_PARTY_NOTICES.md` and replace it with a redistributable asset before the release.
|
||||
- Include "© OpenStreetMap contributors" and the appropriate license link; review ODbL obligations for distributed derived databases.
|
||||
- Do **not** use extracted Google imagery or geometry without explicit applicable rights.
|
||||
- Keep restricted assets out of the public repository; prefer redistributable replacements.
|
||||
- Review Epic distribution requirements before publishing builds.
|
||||
|
||||
|
||||
+16
-10
@@ -80,7 +80,7 @@ LD_LIBRARY_PATH="$B/blender/lib" "$B/blender/blender" -b --factory-startup \
|
||||
```
|
||||
|
||||
- `Road_Hollywood`: 8 120 verts, 7 812 faces, carriageway −10.5…+10.5 m, DEM elevations plus a 2 cm lift so it does not z-fight the terrain.
|
||||
- `Sidewalk_South` 4.0 m wide, `Sidewalk_North` 5.3 m wide, both raised by the kerb height.
|
||||
- `Sidewalk_South` 4.0 m wide, `Sidewalk_North` 5.3 m wide. The kerb upstand is **full at the road-side edge and tapers to zero at the building-side edge**, so the walk surface meets the terrain flush (before `0.1.1` the outer lip stood the full kerb height above the DEM — a 15.8 cm ledge).
|
||||
- `Kerb_South`, `Kerb_North`: vertical faces, mostly **0.13 m** — the 0.15 m assumption minus nothing on the road side — and flushed to 0 within ±2.5 m of each signalised crossing with a 1.5 m blend. Measured from the built mesh: 256 steps at 0.13 m, 15 flush, a few in transition.
|
||||
|
||||
Crown check on the built mesh at mid-block: gutter −10.5 m → +0.99, −5 m → +1.38, centre → +1.52, +5 m → +1.58, +10.5 m → +1.52. The crown sits just north of the centreline, as the cross-sections said.
|
||||
@@ -103,16 +103,20 @@ Objects are appended from the source files, not regenerated, and grouped into `S
|
||||
|
||||
A downward raycast on a 1 m grid along both sidewalks and the designated crossing, ignoring blockout meshes so marquees and canopies overhead do not count as ground:
|
||||
|
||||
| Route | Samples | Holes | Z range | Worst step |
|
||||
| ----------------- | ------: | ----: | ------------- | ---------: |
|
||||
| South sidewalk | 279 | 0 | −0.45 … +3.13 | 20.5 cm |
|
||||
| North sidewalk | 279 | 0 | +0.07 … +3.83 | 15.1 cm |
|
||||
| Crossing at 130 m | 26 | 0 | +1.16 … +1.80 | 8.6 cm |
|
||||
| Route | Samples | Holes | Z range | Worst step |
|
||||
| ----------------- | ------: | ----: | ------------- | ----------------: |
|
||||
| South sidewalk | 279 | 0 | −0.48 … +3.06 | 13.4 cm (s=198) |
|
||||
| North sidewalk | 279 | 0 | +0.06 … +3.76 | 6.9 cm (s=33) |
|
||||
| Crossing at 130 m | 26 | 0 | +1.14 … +1.80 | 9.0 cm (off −8.5) |
|
||||
|
||||
At `--sample 0.25` the same routes measure 11.6 cm / 8.0 cm / 3.4 cm. The pre-check now prints the grade and the **location** of the worst step, and its coverage no longer shrinks with `--sample`.
|
||||
|
||||
No holes anywhere, so nothing drops the player on either side. The two flagged steps:
|
||||
|
||||
- **15.8 cm at s≈207**, where a sample crosses from `Terrain_DEM` onto `Sidewalk_South` — that is the sidewalk's outer edge, the 0.15 m kerb height minus the terrain. Mostly hidden by buildings, but it is a real edge and it will be visible anywhere a building is missing.
|
||||
- **10–13 cm steps at s≈33, 39 and 126**, inside the sidewalk mesh: the kerb-flush transition at the crossings. That is a ~10 % ramp over 1.5 m, steeper than a real crossing ramp (8 % max). Worth grading properly when the crossings are detailed.
|
||||
- ~~15.8 cm at s≈207, the sidewalk's outer edge~~ — **fixed in `0.1.1`**: the sidewalk upstand now tapers to zero at the building side, so the outer edge is flush with the terrain (north route 15.1 → 6.9 cm).
|
||||
- ~~10–13 cm steps at the crossings, ~10 % ramp over 1.5 m~~ — **fixed in `0.1.1`**: the ramp run is now derived from a target grade (`--max-ramp-grade`, default 8 %), giving 1.63 m instead of the fixed 1.5 m (8.7 %). Measured at 0.25 m sampling the crossing now steps 3.4 cm (~1.4 % of the route) at the kerb line.
|
||||
|
||||
**Remaining finding (not a crossing, not the outer edge):** the south route still measures a **13.4 cm step at s≈198 m** (11.6 cm at 0.25 m sampling) and the north route 8.0 cm at s≈251 m. Neither is at a crossing and neither is the outer lip, so this is a local discontinuity in the sidewalk strip — most likely the DEM sampling or a centreline vertex near the block ends. Tracked under the `0.1.1` "audit any collision gaps" item rather than left undocumented.
|
||||
|
||||
This is a pre-check, not the `0.1.0` release check: the release check is traversal in the engine.
|
||||
|
||||
@@ -127,13 +131,15 @@ Blender works in metres, and the imported meshes match the OSM geometry, so the
|
||||
| Building | Blender dimensions | OSM way outline | Height source |
|
||||
| --- | --- | --- | --- |
|
||||
| El Capitan Entertainment Centre | 25.72 × 54.47 × 14.50 m | 25.75 × 54.88 m | `height` tag 14.5 |
|
||||
| El Capitan Theatre | 38.66 × 54.46 × 20.96 m | 38.59 × 54.87 m | `building:levels=6` × 3.2 m, plus roof |
|
||||
| El Capitan Theatre | 38.66 × 54.46 × 20.55 m | 38.59 × 54.87 m | `building:levels=6` × **3.1 m**, plus ~1.9 m roof |
|
||||
| Madame Tussaud's | 42.48 × 56.77 × 15.90 m | 42.0 m frontage | `height` tag 15.9 |
|
||||
| TCL Chinese Theatre | 43.78 × 75.90 × 18.10 m | 44.0 m frontage | `height` tag 18.1 |
|
||||
|
||||
Widths agree to a few centimetres; depths differ by up to 0.4 m because Blosm assembles **multipolygon relations**, not the raw way I measured. Placement after the frame correction agrees with the UTM control points to 0.1–0.5 m per building — the residual is relation assembly and Blosm's edge straightening, not projection.
|
||||
|
||||
Buildings **without** a `height` tag get levels × 3.2 m, which is a placeholder, not evidence. Every such height is a modelling decision to be replaced from the reference photography.
|
||||
Buildings **without** a `height` tag get levels × **3.1 m**. The storey height was 3.2 m until 2026-11-09, when a cross-check of every block building against Mapbox's building data (`Geo/tools/mapbox_buildings.py`) showed our figure running 0.6 m high per six levels — i.e. 3.1 m per level — and that calibration was adopted. The few buildings where Mapbox disagreed with a pure `levels × 3.1 m` fallback carry an explicit override in `Geo/tools/block_height_report.py::HEIGHT_OVERRIDES`.
|
||||
|
||||
Blosm adds roof mass on top of `levels × levelHeight`: the two `levels`-derived frontage buildings both land ~1.9 m above their table height (6922 39.1 m against 37.2 m, El Capitan Theatre 20.5 m against 18.6 m). That offset is the roof, not a height error — the table records the tagged height, the mesh records the storey plus its roof.
|
||||
|
||||
## Relation completeness
|
||||
|
||||
|
||||
Binary file not shown.
@@ -40,7 +40,9 @@ def parse_args():
|
||||
ap.add_argument("--osm", required=True, help="local .osm file to import")
|
||||
ap.add_argument("--bounds", required=True, help="south,west,north,east in WGS84 degrees")
|
||||
ap.add_argument("--out", required=True, help="output .blend path")
|
||||
ap.add_argument("--level-height", type=float, default=3.2, help="metres per building level")
|
||||
# 3.1 m, not 3.2: calibrated against Mapbox building data on 2026-11-09
|
||||
# (see Geo/tools/mapbox_buildings.py and Geo/tools/block_height_report.py).
|
||||
ap.add_argument("--level-height", type=float, default=3.1, help="metres per building level")
|
||||
ap.add_argument("--origin", default=None,
|
||||
help="lat,lon to use as the Blender origin (project origin). Default: Blosm centres on the extract")
|
||||
ap.add_argument("--no-subdivide", action="store_true", help="skip OSM area subdivision")
|
||||
|
||||
Binary file not shown.
@@ -3,18 +3,18 @@
|
||||
"frame": "project frame, metres, X east Y north Z up",
|
||||
"bounds_m": {
|
||||
"min": [
|
||||
-13.02,
|
||||
-13.08,
|
||||
-12.5,
|
||||
0.0
|
||||
],
|
||||
"max": [
|
||||
13.02,
|
||||
0.07,
|
||||
13.08,
|
||||
0.13,
|
||||
14.5
|
||||
],
|
||||
"size": [
|
||||
26.04,
|
||||
12.57,
|
||||
26.16,
|
||||
12.63,
|
||||
14.5
|
||||
]
|
||||
},
|
||||
@@ -33,10 +33,28 @@
|
||||
"dims_m": [
|
||||
0.95,
|
||||
0.95,
|
||||
7.45
|
||||
8.25
|
||||
],
|
||||
"evidence": "6 columns at 4.96 m centres (sibling notes x measured frontage)"
|
||||
},
|
||||
{
|
||||
"object": "Hero_ColumnBase_x6",
|
||||
"dims_m": [
|
||||
1.36,
|
||||
1.36,
|
||||
0.22
|
||||
],
|
||||
"evidence": "assumption: Ionic base moulding; photographs show a wider base at the shaft foot"
|
||||
},
|
||||
{
|
||||
"object": "Hero_Abacus_x6",
|
||||
"dims_m": [
|
||||
1.3,
|
||||
1.3,
|
||||
0.14
|
||||
],
|
||||
"evidence": "assumption: square abacus under the architrave, as Ionic capitals carry"
|
||||
},
|
||||
{
|
||||
"object": "Hero_Architrave",
|
||||
"dims_m": [
|
||||
@@ -55,12 +73,21 @@
|
||||
],
|
||||
"evidence": "assumption: projecting cornice line"
|
||||
},
|
||||
{
|
||||
"object": "Hero_Dentil_xN",
|
||||
"dims_m": [
|
||||
0.18,
|
||||
0.3,
|
||||
0.22
|
||||
],
|
||||
"evidence": "52 dentils at 0.45 m centres under the corona; assumption: photographed cornice reads as a dentilled course"
|
||||
},
|
||||
{
|
||||
"object": "Hero_Entablature",
|
||||
"dims_m": [
|
||||
25.75,
|
||||
1.5,
|
||||
3.4
|
||||
3.0
|
||||
],
|
||||
"evidence": "assumption: entablature depth"
|
||||
},
|
||||
@@ -69,7 +96,7 @@
|
||||
"dims_m": [
|
||||
25.75,
|
||||
1.2,
|
||||
2.2
|
||||
1.8
|
||||
],
|
||||
"evidence": "assumption: parapet carries the motto band"
|
||||
},
|
||||
@@ -86,10 +113,10 @@
|
||||
"object": "Hero_Medallion_x2",
|
||||
"dims_m": [
|
||||
1.1,
|
||||
1.1,
|
||||
0.02
|
||||
0.04,
|
||||
1.1
|
||||
],
|
||||
"evidence": "photographs: two stone discs beside the motto"
|
||||
"evidence": "photographs: two stone discs beside the motto, mounted on the parapet face"
|
||||
},
|
||||
{
|
||||
"object": "Hero_Stylobate",
|
||||
|
||||
@@ -15,6 +15,9 @@ import os
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
sys.path.insert(0, os.path.join(os.path.join(os.path.dirname(__file__), "..", "..", ".."), "Tools"))
|
||||
import fbx_export_preset as fbx_preset # noqa: E402
|
||||
|
||||
|
||||
MESHES = ("Terrain_DEM", "Road_Hollywood", "Sidewalk_South", "Sidewalk_North",
|
||||
"Kerb_South", "Kerb_North", "Crossing_Markings")
|
||||
@@ -60,20 +63,7 @@ def main():
|
||||
_ = bpy.context.view_layer
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
path = os.path.join(args.out, name + ".fbx")
|
||||
bpy.ops.export_scene.fbx(
|
||||
filepath=path,
|
||||
use_selection=True,
|
||||
object_types={"MESH"},
|
||||
apply_unit_scale=True,
|
||||
apply_scale_options="FBX_SCALE_NONE",
|
||||
axis_forward="-Y",
|
||||
axis_up="Z",
|
||||
use_space_transform=True,
|
||||
use_mesh_modifiers=True,
|
||||
mesh_smooth_type="OFF",
|
||||
path_mode="AUTO",
|
||||
bake_space_transform=False,
|
||||
)
|
||||
fbx_preset.export_static(path, selection=True)
|
||||
if mirrored:
|
||||
# The flip is an export correction, not a change to the source: undo it.
|
||||
obj.scale.y *= -1.0
|
||||
|
||||
Binary file not shown.
@@ -165,10 +165,16 @@ def main():
|
||||
def lat_lon(s, offset):
|
||||
return oc.point_at(line, _lengths, s, offset, lat0, lon0)
|
||||
|
||||
# Sample spacing is independent of coverage: walk the whole block at
|
||||
# `--sample` metres, so a finer spacing measures the gradient instead of
|
||||
# accidentally shortening the route (previously range(int(block_len)) with a
|
||||
# 0.25 m spacing only covered a quarter of the block).
|
||||
along = int(block_len / args.sample)
|
||||
across = int(26.0 / args.sample)
|
||||
route_specs = {
|
||||
"south sidewalk": [(s, -12.5) for s in [i * args.sample for i in range(int(block_len))]],
|
||||
"north sidewalk": [(s, 13.0) for s in [i * args.sample for i in range(int(block_len))]],
|
||||
"crossing at 130 m": [(130.0, o) for o in [i * args.sample - 12.5 for i in range(26)]],
|
||||
"south sidewalk": [(i * args.sample, -12.5) for i in range(along)],
|
||||
"north sidewalk": [(i * args.sample, 13.0) for i in range(along)],
|
||||
"crossing at 130 m": [(130.0, i * args.sample - 12.5) for i in range(across)],
|
||||
}
|
||||
flat = [(spec, lat_lon(s, off)) for spec, samples in route_specs.items() for s, off in samples]
|
||||
local = project_to_local([ll for _spec, ll in flat], find_proj())
|
||||
@@ -185,12 +191,20 @@ def main():
|
||||
heights.append(None)
|
||||
else:
|
||||
heights.append(z)
|
||||
deltas = [abs(b - a) for a, b in zip(heights, heights[1:])
|
||||
deltas = [(i, abs(b - a)) for i, (a, b) in enumerate(zip(heights, heights[1:]))
|
||||
if a is not None and b is not None]
|
||||
max_step = max(deltas) if deltas else float("nan")
|
||||
worst_i, max_step = (max(deltas, key=lambda t: t[1]) if deltas else (0, float("nan")))
|
||||
known = [h for h in heights if h is not None]
|
||||
grade = (max_step / args.sample) * 100 if deltas else float("nan")
|
||||
# Position of the worst step, as arc length / cross offset, so the cause
|
||||
# can be located instead of guessed.
|
||||
if label == "crossing at 130 m":
|
||||
where = f"s=130 off={(worst_i * args.sample - 12.5):+.2f} m"
|
||||
else:
|
||||
where = f"s={worst_i * args.sample:.1f} m"
|
||||
print(f" {label:18} samples {len(heights):3} holes {holes} "
|
||||
f"Z {min(known):+.2f}..{max(known):+.2f} max step {max_step*100:.1f} cm")
|
||||
f"Z {min(known):+.2f}..{max(known):+.2f} max step {max_step*100:.1f} cm "
|
||||
f"({grade:.1f} % grade) at {where}")
|
||||
|
||||
out = os.path.abspath(args.out)
|
||||
os.makedirs(os.path.dirname(out), exist_ok=True)
|
||||
|
||||
Binary file not shown.
@@ -68,8 +68,13 @@ def parse_args():
|
||||
ap.add_argument("--cross", type=float, default=0.75)
|
||||
ap.add_argument("--kerb", type=float, default=0.15)
|
||||
ap.add_argument("--ramp-half-width", type=float, default=2.5)
|
||||
ap.add_argument("--ramp-blend", type=float, default=1.5)
|
||||
return ap.parse_args(argv)
|
||||
ap.add_argument("--ramp-blend", type=float, default=None,
|
||||
help="override the ramp run; default derives it from --max-ramp-grade")
|
||||
ap.add_argument("--max-ramp-grade", type=float, default=0.08,
|
||||
help="maximum crossing-ramp slope (0.08 = 8%%)")
|
||||
args = ap.parse_args(argv)
|
||||
args.ramp_blend = ramp_blend(args.kerb, args.max_ramp_grade, args.ramp_blend)
|
||||
return args
|
||||
|
||||
|
||||
def find_proj():
|
||||
@@ -125,7 +130,13 @@ class Dem:
|
||||
|
||||
|
||||
def kerb_height(s, base, half_width, blend):
|
||||
"""Full kerb, except flushed near a crossing so people can step off."""
|
||||
"""Full kerb, except flushed near a crossing so people can step off.
|
||||
|
||||
`blend` is the horizontal run over which the kerb drops from `base` to the
|
||||
0.02 m flush height. It is derived from a target maximum grade (see
|
||||
`ramp_blend`) so the crossing ramps stay within a real ramp's slope instead
|
||||
of the ~8.7 % the old fixed 1.5 m run produced.
|
||||
"""
|
||||
for crossing in CROSSINGS:
|
||||
d = abs(s - crossing)
|
||||
if d <= half_width:
|
||||
@@ -136,6 +147,22 @@ def kerb_height(s, base, half_width, blend):
|
||||
return base
|
||||
|
||||
|
||||
def ramp_blend(kerb, max_grade, override=None):
|
||||
"""Horizontal run needed for the kerb ramp to stay at or under max_grade.
|
||||
|
||||
0.15 m kerb at an 8 % grade needs 1.63 m of run; the previous fixed 1.5 m
|
||||
gave 8.7 %, and the walk pre-check measured the transition as a 10-13 cm
|
||||
step over 1.5 m. When `override` is given it wins (kept for reproducing old
|
||||
meshes), otherwise the run is computed from the live kerb height so it stays
|
||||
correct if the kerb height assumption changes.
|
||||
"""
|
||||
if override is not None:
|
||||
return max(0.0, override)
|
||||
if max_grade <= 0.0:
|
||||
return 0.0
|
||||
return max(0.0, (kerb - 0.02)) / max_grade
|
||||
|
||||
|
||||
def strip(rows, offsets, z_at, name, material):
|
||||
"""Build a quad grid from per-row sampled vertices."""
|
||||
verts, faces = [], []
|
||||
@@ -218,13 +245,31 @@ def main():
|
||||
z = dem_z(x, y)
|
||||
return (z if z is not None else 0.0) + 0.02
|
||||
|
||||
def south_z(r, off, x, y):
|
||||
def sidewalk_z(r, off, inner, outer, x, y):
|
||||
"""Sidewalk surface: full kerb upstand at the road-side (inner) edge,
|
||||
tapering to zero at the building-side (outer) edge so the walk surface
|
||||
meets the terrain flush.
|
||||
|
||||
Before this taper the outer lip stood the full kerb height above the DEM,
|
||||
which the walkability pre-check measured as a 15.8 cm step where a sample
|
||||
crosses from Terrain_DEM onto Sidewalk_South (with a matching lip on the
|
||||
north side). A 0.15 m lip over a 4.0 m / 5.3 m strip is a ~3-4 % ramp:
|
||||
walkable and visually negligible, and it removes the ledge entirely.
|
||||
"""
|
||||
z = dem_z(x, y)
|
||||
base = z if z is not None else 0.0
|
||||
return base + kerb_height(rows[r][0], args.kerb, args.ramp_half_width, args.ramp_blend)
|
||||
h = kerb_height(rows[r][0], args.kerb, args.ramp_half_width, args.ramp_blend)
|
||||
span = inner - outer
|
||||
if abs(span) < 1e-9:
|
||||
return base + h
|
||||
t = min(1.0, max(0.0, (off - outer) / span))
|
||||
return base + h * t
|
||||
|
||||
def south_z(r, off, x, y):
|
||||
return sidewalk_z(r, off, SOUTH_GUTTER, SOUTH_SIDEWALK, x, y)
|
||||
|
||||
def north_z(r, off, x, y):
|
||||
return south_z(r, off, x, y)
|
||||
return sidewalk_z(r, off, NORTH_GUTTER, NORTH_SIDEWALK, x, y)
|
||||
|
||||
road = strip(rows, road_offsets, road_z, "Road_Hollywood", asphalt)
|
||||
sw_s = strip(rows, south_offsets, south_z, "Sidewalk_South", concrete)
|
||||
|
||||
+174
-3
@@ -2,8 +2,179 @@
|
||||
|
||||
All notable changes to this project are documented here.
|
||||
|
||||
Format: [Keep a Changelog](https://keepachangelog.com/) style, versions follow [Semantic Versioning](https://semver.org/). Entries are added when a version section in `AGENTS.md` is completed.
|
||||
Format: [Keep a Changelog](https://keepachangelog.com/) style, versions follow [Semantic Versioning](https://semver.org/). Entries are added when a version section in `TODO.md` is completed.
|
||||
|
||||
## Unreleased
|
||||
## [Unreleased]
|
||||
|
||||
No completed releases yet.
|
||||
Nothing yet.
|
||||
|
||||
## [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.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Map imports could report success while saving nothing.** `.umap` files are LFS-tracked and check out read-only, so `save_map()` returned `False` with no error and the staged actor vanished on the next load. New `Tools/ue_common.py` (`make_writable`, `require_saved`) clears the read-only bit and makes the failure loud; `ue_hero_facade_import.py` and `ue_import_north_buildings.py` now use it. Verified from a forced `0o444` map: chmod logged, `save_map: True`, exit 0.
|
||||
|
||||
### Changed
|
||||
|
||||
- `Docs/build.md`: content-pipeline table now lists every step as it exists (seven site FBXs, frontage, north buildings, hero, map) and the shared FBX preset; packaging archive corrected to `Builds/GigLineQA`; the packaged log location and `-qaselftest` usage are documented; new **traps** table covers the read-only save, the hero `--out`-is-the-FBX mistake, the MPFB add-on path, the shot actor quitting under QA, and the null-RHI trace trap.
|
||||
- `Docs/setup.md`: the fresh-checkout build is marked verified (2026-11-08) and its blocker closed; a new section documents the two optional keys (`OPENROUTER_API_KEY`, `MAPBOX_ACCESS_TOKEN`) and that neither is needed by the build, the tests or a release.
|
||||
- `Docs/Architecture/07_deployment_view.md` and the combined `architecture.md`: build-output path corrected and marked as living outside the repository.
|
||||
|
||||
### Verified
|
||||
|
||||
- 54 tool tests pass (5 new for `ue_common`).
|
||||
- Packaged build still launches into `DemoMap` and `-qaselftest` passes every phase.
|
||||
|
||||
## [0.1.2] — 2026-11-09
|
||||
|
||||
Scale and façade correction patch. Buildings and the hero elevation now match the reference photography and the cross-checked height data; no new gameplay scope.
|
||||
|
||||
### Added
|
||||
|
||||
- **`Tools/facade_detail_concepts.py`** — generates frontal building plates (whole elevation, entrance in its façade, building with neighbours) from a local reference photograph through OpenRouter image models. Spec-driven, never overwrites, writes a provenance sidecar per image, output stays in the gitignored `Art/reference/` tree. Local working material only.
|
||||
- **`Tools/fbx_export_preset.py`** — the single FBX export preset (`ue_metric_v1` / `ue_character_v1`). Replaces the argument block that had been copied into six exporters.
|
||||
- **Façade occlusion probe** in `-qaselftest`: 40 m traces sideways from the road centre at walking height, required wherever a building is staged.
|
||||
- **Ionic detail on the hero**: base moulding at every shaft foot, a square abacus over every capital, and a 52-dentil cornice course.
|
||||
- **Tool tests**: `Tools/tests/` now covers the input driver, height classification, the Mapbox cross-check, the concept generator and the FBX preset (49 tests).
|
||||
|
||||
### Changed
|
||||
|
||||
- **Storey height 3.2 → 3.1 m**, calibrated against Mapbox building data, with explicit overrides for three rear outbuildings; the block cross-check now reports 66 matches and no disagreements.
|
||||
- **Hero colonnade 47.6 % → 53.1 %** of the 14.50 m elevation, inside the photographed 50–60 % band (thinner entablature and parapet).
|
||||
- Blockout regenerated at the new storey height; frontage re-exported and re-imported (6922 42.1 → 39.1 m, El Capitan Theatre 20.0 → 20.5 m).
|
||||
- FBX exports now carry smoothing groups (`FACE` instead of `OFF`), so round geometry is no longer faceted and imports stop warning.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Windows floated 1.44 m in front of the wall**, ahead of the colonnade; they now sit on the wall plane behind the columns.
|
||||
- **Parapet medallions were flat discs lying on the coping** instead of discs on the parapet face.
|
||||
- **The hero façade had no usable collision** — the player could walk through it. It is now complex-as-simple; auto-generated convex hulls were tried and rejected because they bulged into the street.
|
||||
- **The three "guessed" block heights were never buildings**: two `building=roof` canopies and a transit shelter. They are classified as non-building volumes and the block now reports zero guessed heights.
|
||||
|
||||
### Verified
|
||||
|
||||
- Packaged build `-qaselftest`: walk, reset and pause pass; seam transect 1456 samples / 0 holes; wall probe 0/35; façade probe 0/9.
|
||||
- Screenshots re-rendered for every change and approved by the owner.
|
||||
|
||||
### Known limitations
|
||||
|
||||
- Cornice dentil spacing, the base and abacus dimensions are recorded assumptions: the reference photography does not resolve them at our scale.
|
||||
- Façade surfaces remain flat blockout materials; the textured pass is `0.1.4`.
|
||||
|
||||
## [0.1.1] — 2026-11-08
|
||||
|
||||
Collision-fidelity patch. No new scope: street geometry corrections and the QA tooling that measures them.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Sidewalk outer edge ledge.** The sidewalk stood the full 0.15 m kerb height above the DEM at both edges, leaving a 15.8 cm lip where the walk route crossed from terrain onto the south sidewalk. The upstand now tapers from full at the road-side edge to zero at the building-side edge; the north route's worst step fell from 15.1 cm to 6.9 cm.
|
||||
- **Crossing ramps too steep.** The kerb-flush run was a fixed 1.5 m, giving ~8.7 % (measured 10–13 cm over 1.5 m). The run is now derived from a target grade (`--max-ramp-grade`, default 8 % ⇒ 1.63 m); the crossing steps 3.4 cm at 0.25 m sampling.
|
||||
|
||||
### Added
|
||||
|
||||
- **Walk pre-check improvements** (`Art/blender/site/site_assembly.py`): sampling coverage is independent of `--sample` (it previously shrank the route), and the report now prints the grade and the arc-length/offset **location** of the worst step, so a step can be located rather than guessed.
|
||||
|
||||
### Changed
|
||||
|
||||
- **Kerb height recorded as final** at 0.15 m modelled / 0.13 m as built — a standard 6 in LA kerb; neither the 3DEP DEM nor the reference photography can resolve it further (±2 cm), which is far below the character's 45 cm step height.
|
||||
|
||||
### Verified
|
||||
|
||||
- Site meshes rebuilt and re-imported (7/7, collision flags intact), DemoMap rebuilt, packaged Development build `EXIT=0`.
|
||||
- `-qaselftest` on the packaged build: walk 761 cm in 2 s at steady Z 456, reset 0 cm, pause/unpause, transect 1456 samples across 16 rows / **0 holes**, wall probe **0/35** blocking hits.
|
||||
|
||||
### Known limitations
|
||||
|
||||
- One residual **13.4 cm surface step at s≈198 m** on the south sidewalk (8.0 cm at s≈251 m north). It is not a gap and is below the 45 cm `MaxStepHeight`, so it does not block traversal; the cause is a sidewalk-strip discontinuity near a centreline vertex, recorded in `Art/blender/README.md` as a smoothness refinement.
|
||||
- Kerb radii at junctions, driveway aprons, drainage inlets and pavement joints remain unmodelled (unchanged from `0.1.0`).
|
||||
|
||||
Nothing yet.
|
||||
|
||||
## [0.1.0] — 2026-11-08
|
||||
|
||||
The first playable release: a walkable exterior demo of the **6840 Hollywood Blvd** (El Capitan Entertainment Centre) block in Unreal Engine, built on OpenStreetMap layout data and USGS elevation, with the hero façade and the verified block frontage staged on a walkable street.
|
||||
|
||||
### Added
|
||||
|
||||
- **Unreal project** (`Game/GigLine`) — C++ game module, Enhanced Input, packaged Linux build, and a `DemoMap` that a plain launch opens directly.
|
||||
- **Walkable demo level** — terrain from the 3DEP DEM, Hollywood Blvd carriageway, both sidewalks and kerbs with collision, the designated mid-block crossing, and fixed daytime lighting (sun, sky light, sky atmosphere, height fog, pinned exposure).
|
||||
- **Hero façade** for 6840 Hollywood Blvd — six-column neoclassical elevation with three genuine portal recesses, entablature, parapet and motto band (`Tools/hero_facade_build.py`, `Tools/ue_hero_facade_import.py`).
|
||||
- **Block frontage** — the five verified south-side neighbours (6922, 6902, 6838 El Capitan Theatre, 6822 Budget, 6806–6822, 6800 Lee Drug) and the two north-side buildings (TCL Chinese Theatre, Madame Tussaud's) placed at their OSM positions.
|
||||
- **First-person player** — `AGigLineCharacter`: 1.75 m capsule, 1.60 m eye camera, 4 m/s walk, mouse look, jump, reset (`R`) and pause (`Escape`); controls documented in `Docs/controls.md` and shown in-game.
|
||||
- **In-game ODbL attribution** — `AGigLineHUD` draws the OpenStreetMap credit and licence line every frame.
|
||||
- **QA harness** — `-qaselftest` (walk, reset, pause, seam transect, invisible-wall probe, frame-time capture) and `Tools/input_test.py` / `Tools/input_emulation.py` for OS-level virtual keyboard/mouse input.
|
||||
- **Documentation** — setup, build, toolchain, licensing, ODbL review, release checks, and arc42 architecture docs.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **All controls were dead in packaged and PIE builds.** The mapping-context generator wrote key bindings to the deprecated `mappings` array; UE 5.8 reads `DefaultKeyMappings.Mappings`, so the cooked game had zero runtime mappings and every key resolved to nothing. `Tools/ue_create_input_assets.py` now populates the current field.
|
||||
- **"Fall-through" was an 8 m invisible box.** `Terrain_DEM` shipped only a bounding-box collision proxy; forcing complex-as-simple collision (`UGigLineAssetTools`) put the player on the actual street.
|
||||
- **Reset returned the player to mid-air** — the start position is now captured on the first grounded frame.
|
||||
- **Plain launch opened the engine template** — the default map now lives in `DefaultEngine.ini` and points at `DemoMap`.
|
||||
- **Game target would not compile** — removed editor-only API use (`UStaticMesh::PostEditChange`, `AActor::GetActorLabel`).
|
||||
- **`demo-north-buildings` screenshot** framed 85 % sky (camera 9.5 km west of the buildings); re-pointed to the building mass.
|
||||
|
||||
### Verified
|
||||
|
||||
- Both sides of the demo are traversable: 1456-sample seam transect, 0 holes, no blocking collision or invisible walls (`-qaselftest` on the packaged build).
|
||||
- Placement and street scale match the reference photography and Geo measurements (road 24.3 m, sidewalk offsets 12.5/13.2 m, hero frontage 26.0 m).
|
||||
- **414 fps average / p99 3.01 ms at 2560×1440** (Epic preset, TSR, VSync off, no frame generation) — far above the 60 fps target.
|
||||
- Fresh checkout builds and packages with the documented external engine (UE 5.8.4).
|
||||
|
||||
### Known limitations
|
||||
|
||||
- Buildings other than the hero are flat blockout volumes; façade detail, materials and Blosm style scripts are planned for `0.1.4`.
|
||||
- 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.4...main
|
||||
[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
|
||||
[0.1.0]: https://git.allucanget.biz/zwitschi/gig-line/releases/tag/v0.1.0
|
||||
@@ -9,7 +9,6 @@ Gig Line is a Linux-first Unreal Engine 5 game that reconstructs a walkable (and
|
||||
| F-1 | Walk both sides of Hollywood Blvd in the demo area without falling through geometry |
|
||||
| F-2 | First-person movement (WASD + mouse look), jump, reset (R), pause (Escape) |
|
||||
| F-3 | Fixed daytime lighting and atmosphere (sun, sky, fog, exposure) |
|
||||
| F-4 | Geographic attribution display (ODbL: "© OpenStreetMap contributors" + licence link) |
|
||||
| F-5 | Designated street crossing with zebra markings at signalised crossings |
|
||||
| F-6 | Building façades recognisable against reference photography |
|
||||
| F-7 | Terrain, road, and sidewalk follow real-world DEM elevation |
|
||||
@@ -25,7 +24,7 @@ Full requirements and progress are in [TODO.md](../../TODO.md); version directio
|
||||
| 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 |
|
||||
| 4 | **Build reproducibility** | Fresh checkout + documented engine path produces a runnable Linux build |
|
||||
| 5 | **Licensing compliance** | Every redistributable asset has verified provenance; ODbL attribution is visible at runtime |
|
||||
| 5 | **Provenance** | Every committed asset has recorded provenance |
|
||||
|
||||
## Stakeholders
|
||||
|
||||
@@ -34,4 +33,3 @@ Full requirements and progress are in [TODO.md](../../TODO.md); version directio
|
||||
| Owner / Director | zwitschi | Sets scope, approves screenshots, cuts release tags |
|
||||
| AI coding agents | multiple sessions | Read `AGENTS.md`, `TODO.md`, `ROADMAP.md`, and this document for architecture constraints; never commit directly to `main` |
|
||||
| Players | end users | Walk the demo at 60 FPS without falling through the world; see a recognisable Hollywood block |
|
||||
| OSM contributors | OpenStreetMap | ODbL attribution is displayed and derivative-database obligations are met |
|
||||
@@ -7,7 +7,7 @@ The system is a self-contained single-player game. It has no server backend, no
|
||||
| Partner | Inbound | Outbound |
|
||||
| --- | --- | --- |
|
||||
| Player (keyboard/mouse) | WASD, mouse, R (reset), Escape (pause), Space (jump) | Rendered 3D scene + HUD |
|
||||
| OSM data | Extracted building footprints, street geometry, elevation | Attribution string at runtime |
|
||||
| OSM data | Extracted building footprints, street geometry, elevation | — |
|
||||
| Filesystem | Engine content (`.uasset`, `.umap`), Blender sources (`.blend`), DEM GeoTIFF | Saved maps, screenshots, packaged build |
|
||||
|
||||
## Technical Context
|
||||
|
||||
@@ -20,7 +20,7 @@ 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` | ODbL attribution overlay | `DrawText` every frame, bottom-left |
|
||||
| `AGigLineHUD` | HUD text overlay | `DrawText` every frame, bottom-left |
|
||||
| `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 |
|
||||
@@ -60,8 +60,11 @@ Python scripts in `Tools/` drive the content pipeline:
|
||||
| `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/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 |
|
||||
|
||||
Source data: `Art/blender/` (`.blend` files), `Geo/` (OSM extracts, DEM, verification docs).
|
||||
Source data: `Art/blender/` (`.blend` files), `Geo/` (OSM extracts, DEM, verification docs). Tool rules (one script one job, result files for commandlets, clean worktree for generators, documented inventory, tests for the pure parts) are in [`AGENTS.md`](../../AGENTS.md#python-and-shell-tooling).
|
||||
|
||||
### QA & Review
|
||||
|
||||
@@ -75,15 +78,15 @@ Source data: `Art/blender/` (`.blend` files), `Geo/` (OSM extracts, DEM, verific
|
||||
|
||||
### White Box Gameplay — Input Flow
|
||||
|
||||
1. `AGigLineCharacter::SetupPlayerInputComponent` binds `IA_*` actions to `MoveForward`, `MoveBack`, `MoveStrafe`, `MoveLeft`, `Look`, `StartJump`/`StopJump`, `ResetToStart`, `TogglePause`.
|
||||
1. `AGigLineCharacter::SetupPlayerInputComponent` binds `IA_*` actions to `MoveForward`, `MoveBack`, `MoveStrafe`, `MoveLeft`, `Look`, `StartJump`/`StopJump`, `ResetToStart`, `TogglePause`.
|
||||
|
||||
2. Movement delegates to `ApplyMove(Forward, Strafe)` → `AddMovementInput`.
|
||||
2. Movement delegates to `ApplyMove(Forward, Strafe)` → `AddMovementInput`.
|
||||
|
||||
3. Look applies yaw/pitch scaled by `LookSensitivity` (Y invertable via `bInvertLookY`).
|
||||
3. Look applies yaw/pitch scaled by `LookSensitivity` (Y invertable via `bInvertLookY`).
|
||||
|
||||
4. `ResetToStart` teleports to `StartLocation` (captured on first grounded frame), clears velocity, resets control rotation.
|
||||
4. `ResetToStart` teleports to `StartLocation` (captured on first grounded frame), clears velocity, resets control rotation.
|
||||
|
||||
5. `TogglePause` toggles `APlayerController::SetPause`, swaps input mode between `FInputModeGameOnly` and `FInputModeUIOnly`, shows/hides mouse cursor.
|
||||
5. `TogglePause` toggles `APlayerController::SetPause`, swaps input mode between `FInputModeGameOnly` and `FInputModeUIOnly`, shows/hides mouse cursor.
|
||||
|
||||
### White Box Site — Collision Model
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
| Dev workstation | Bazzite Linux 45 (Fedora 45 base, immutable ostree), Ryzen 7 7700X, 64 GB RAM, RTX 5070 Ti 16 GB, Vulkan RHI |
|
||||
| Engine install | `/run/media/system/DATA/Engines/UE_5.8.4` (73 GiB, external to repo) |
|
||||
| Project repo | `/run/media/system/DATA/Documents/06-games/gig-line` (Git + LFS, remote at `git.allucanget.biz`) |
|
||||
| Build output | `/run/media/system/DATA/Builds/GigLine` (packaged Linux build, ~1.1 GiB) |
|
||||
| Build output | `/run/media/system/DATA/Builds/GigLineQA/Linux` (packaged Development build, ~1.1 GiB; outside the repository) |
|
||||
|
||||
Motivation
|
||||
Single-machine development. No build farm, no CI pipeline yet. The engine, toolchain, and GPU are all on the same host.
|
||||
|
||||
@@ -46,8 +46,6 @@ Rule: place a building only against a neighbour or street mesh, never in isolati
|
||||
|
||||
- Reference photography (local, gitignored) drives manual façade work.
|
||||
|
||||
- In-game ODbL attribution drawn by `AGigLineHUD` every frame, bottom-left.
|
||||
|
||||
## QA harness
|
||||
|
||||
- `-qaselftest`: core ticker (not world timer) so pause cannot freeze the test that proves pause works.
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
| 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 |
|
||||
| Compliance | ODbL attribution visible at runtime | 2 |
|
||||
| Maintainability | Fresh checkout builds with documented engine installation | 2 |
|
||||
| Portability | Linux build packages and runs; Windows build ships | 3 |
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
| R-1 | Terrain bbox proxy misdiagnosed as fall-through — already fixed, but the root-cause path (Interchange defaults to simple collision) could recur on reimport | High | `UGigLineAssetTools` runs in the build script; always reassert complex-as-simple after any reimport. Documented in `MEMORY.md` and ADR-3. |
|
||||
| R-2 | Blosm 3Dsimple on Blender 5.2 — worked around, not resolved; `3Drealistic` mode untested | Medium | Pin Blosm 2.7.28 and Blender 5.2 LTS; test realistic mode before depending on it. |
|
||||
| R-3 | Kerb height is an assumption (DEM does not resolve it) | Medium | Record the assumption in `Geo/verification/`; measure from reference photography if possible; mark as unresolved until verified. |
|
||||
| R-4 | ODbL derivative-database posture for shipped builds is open | Medium | In-game attribution is done; legal review of shipped-DB obligations needed before public release. |
|
||||
| R-5 | `GigLine SIM` / `GigLine GROUND` TEMP diagnostic logging still in `GigLineCharacter.cpp` | Medium | Strip behind a cvar or remove entirely before the `v0.1.0` release tag. |
|
||||
| TD-1 | Materials are Unreal-only — no Blender preview round-trip | Low | Accepted (ADR-2). Look-dev happens in UE; screenshot review catches issues. |
|
||||
| TD-2 | No CI pipeline — all builds and QA are manual on the dev workstation | Low | Acceptable for `0.x`. CI is a `0.3.0+` concern. |
|
||||
@@ -11,7 +11,6 @@
|
||||
| EPSG:26911 | UTM zone 11N, the projected CRS used for the demo area. Local origin recorded in `Geo/`. |
|
||||
| Hero façade | The detailed, reference-calibrated model of the El Capitan building frontage. |
|
||||
| MPFB | MakeHuman Plugin for Blender (2.0.18). Generates parametric human base meshes with rigs and baked materials. |
|
||||
| ODbL | Open Database Licence 1.0. The licence covering OSM-derived data. Requires attribution and may impose share-alike on derivative databases. |
|
||||
| PlayerStart | Unreal actor that defines where the player pawn spawns. Positioned above grade so the capsule does not embed in the floor on spawn. |
|
||||
| QA self-test | The `-qaselftest` command-line switch that runs an automated behaviour check (walk, reset, pause, ground transect) via a core ticker. |
|
||||
| Site | The terrain, road, sidewalks, kerbs, and crossing markings — the ground-level infrastructure of the demo area. |
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# Gig Line Architecture Documentation
|
||||
|
||||
> Linux-first walking/driving demo around 6840 Hollywood Blvd
|
||||
|
||||
Version 0.1.0 — 2026-11-08 — initial architecture documentation
|
||||
---
|
||||
title: Gig Line Architecture Documentation
|
||||
description: Architecture documentation for the Gig Line Linux-first Unreal Engine 5 demo around 6840 Hollywood Blvd.
|
||||
status: v0.1.0
|
||||
date: 2026-11-08
|
||||
---
|
||||
|
||||
# Introduction and Goals
|
||||
|
||||
@@ -15,7 +16,6 @@ Gig Line is a Linux-first Unreal Engine 5 game that reconstructs a walkable (and
|
||||
| F-1 | Walk both sides of Hollywood Blvd in the demo area without falling through geometry |
|
||||
| F-2 | First-person movement (WASD + mouse look), jump, reset (R), pause (Escape) |
|
||||
| F-3 | Fixed daytime lighting and atmosphere (sun, sky, fog, exposure) |
|
||||
| F-4 | Geographic attribution display (ODbL: "© OpenStreetMap contributors" + licence link) |
|
||||
| F-5 | Designated street crossing with zebra markings at signalised crossings |
|
||||
| F-6 | Building façades recognisable against reference photography |
|
||||
| F-7 | Terrain, road, and sidewalk follow real-world DEM elevation |
|
||||
@@ -31,7 +31,7 @@ Full requirements and progress are in [TODO.md](../../TODO.md); version directio
|
||||
| 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 |
|
||||
| 4 | **Build reproducibility** | Fresh checkout + documented engine path produces a runnable Linux build |
|
||||
| 5 | **Licensing compliance** | Every redistributable asset has verified provenance; ODbL attribution is visible at runtime |
|
||||
| 5 | **Provenance** | Every committed asset has recorded provenance |
|
||||
|
||||
## Stakeholders
|
||||
|
||||
@@ -40,7 +40,6 @@ Full requirements and progress are in [TODO.md](../../TODO.md); version directio
|
||||
| Owner / Director | zwitschi | Sets scope, approves screenshots, cuts release tags |
|
||||
| AI coding agents | multiple sessions | Read `AGENTS.md`, `TODO.md`, `ROADMAP.md`, and this document for architecture constraints; never commit directly to `main` |
|
||||
| Players | end users | Walk the demo at 60 FPS without falling through the world; see a recognisable Hollywood block |
|
||||
| OSM contributors | OpenStreetMap | ODbL attribution is displayed and derivative-database obligations are met |
|
||||
|
||||
# Architecture Constraints
|
||||
|
||||
@@ -84,7 +83,7 @@ The system is a self-contained single-player game. It has no server backend, no
|
||||
| Partner | Inbound | Outbound |
|
||||
| --- | --- | --- |
|
||||
| Player (keyboard/mouse) | WASD, mouse, R (reset), Escape (pause), Space (jump) | Rendered 3D scene + HUD |
|
||||
| OSM data | Extracted building footprints, street geometry, elevation | Attribution string at runtime |
|
||||
| OSM data | Extracted building footprints, street geometry, elevation | — |
|
||||
| Filesystem | Engine content (`.uasset`, `.umap`), Blender sources (`.blend`), DEM GeoTIFF | Saved maps, screenshots, packaged build |
|
||||
|
||||
## Technical Context
|
||||
@@ -160,7 +159,7 @@ 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` | ODbL attribution overlay | `DrawText` every frame, bottom-left |
|
||||
| `AGigLineHUD` | HUD text overlay | `DrawText` every frame, bottom-left |
|
||||
| `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 |
|
||||
@@ -200,6 +199,9 @@ Python scripts in `Tools/` drive the content pipeline:
|
||||
| `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/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 |
|
||||
|
||||
Source data: `Art/blender/` (`.blend` files), `Geo/` (OSM extracts, DEM, verification docs).
|
||||
|
||||
@@ -215,15 +217,15 @@ Source data: `Art/blender/` (`.blend` files), `Geo/` (OSM extracts, DEM, verific
|
||||
|
||||
### White Box Gameplay — Input Flow
|
||||
|
||||
1. `AGigLineCharacter::SetupPlayerInputComponent` binds `IA_*` actions to `MoveForward`, `MoveBack`, `MoveStrafe`, `MoveLeft`, `Look`, `StartJump`/`StopJump`, `ResetToStart`, `TogglePause`.
|
||||
1. `AGigLineCharacter::SetupPlayerInputComponent` binds `IA_*` actions to `MoveForward`, `MoveBack`, `MoveStrafe`, `MoveLeft`, `Look`, `StartJump`/`StopJump`, `ResetToStart`, `TogglePause`.
|
||||
|
||||
2. Movement delegates to `ApplyMove(Forward, Strafe)` → `AddMovementInput`.
|
||||
2. Movement delegates to `ApplyMove(Forward, Strafe)` → `AddMovementInput`.
|
||||
|
||||
3. Look applies yaw/pitch scaled by `LookSensitivity` (Y invertable via `bInvertLookY`).
|
||||
3. Look applies yaw/pitch scaled by `LookSensitivity` (Y invertable via `bInvertLookY`).
|
||||
|
||||
4. `ResetToStart` teleports to `StartLocation` (captured on first grounded frame), clears velocity, resets control rotation.
|
||||
4. `ResetToStart` teleports to `StartLocation` (captured on first grounded frame), clears velocity, resets control rotation.
|
||||
|
||||
5. `TogglePause` toggles `APlayerController::SetPause`, swaps input mode between `FInputModeGameOnly` and `FInputModeUIOnly`, shows/hides mouse cursor.
|
||||
5. `TogglePause` toggles `APlayerController::SetPause`, swaps input mode between `FInputModeGameOnly` and `FInputModeUIOnly`, shows/hides mouse cursor.
|
||||
|
||||
### White Box Site — Collision Model
|
||||
|
||||
@@ -239,55 +241,55 @@ Source data: `Art/blender/` (`.blend` files), `Geo/` (OSM extracts, DEM, verific
|
||||
|
||||
## Walk the block
|
||||
|
||||
1. Player launches game → `AGigLineGameModeBase` spawns `AGigLineCharacter` at `PlayerStart` `(-13336, -1150, 600)`.
|
||||
1. Player launches game → `AGigLineGameModeBase` spawns `AGigLineCharacter` at `PlayerStart` `(-13336, -1150, 600)`.
|
||||
|
||||
2. Character falls 144 cm to road surface (Z 456) and begins walking. `StartLocation` captured on first grounded frame.
|
||||
2. Character falls 144 cm to road surface (Z 456) and begins walking. `StartLocation` captured on first grounded frame.
|
||||
|
||||
3. Player holds W → `IA_Move` fires → `ApplyMove(1, 0)` → `AddMovementInput(ForwardVector, 1)` → capsule moves at 450 cm/s.
|
||||
3. Player holds W → `IA_Move` fires → `ApplyMove(1, 0)` → `AddMovementInput(ForwardVector, 1)` → capsule moves at 450 cm/s.
|
||||
|
||||
4. Mouse move → `IA_Look` fires → yaw/pitch applied to controller rotation → camera follows (attached to capsule at 160 cm eye height).
|
||||
4. Mouse move → `IA_Look` fires → yaw/pitch applied to controller rotation → camera follows (attached to capsule at 160 cm eye height).
|
||||
|
||||
5. Player presses R → `ResetToStart` → teleport to `StartLocation`/`StartRotation`, `StopMovementImmediately`, reset control rotation.
|
||||
5. Player presses R → `ResetToStart` → teleport to `StartLocation`/`StartRotation`, `StopMovementImmediately`, reset control rotation.
|
||||
|
||||
6. Player presses Escape → `TogglePause` → `SetPause(true)`, switch to `FInputModeUIOnly`, show cursor. World timers stop; core tickers continue.
|
||||
6. Player presses Escape → `TogglePause` → `SetPause(true)`, switch to `FInputModeUIOnly`, show cursor. World timers stop; core tickers continue.
|
||||
|
||||
7. Player presses Escape again → `TogglePause` → `SetPause(false)`, switch to `FInputModeGameOnly`, hide cursor.
|
||||
7. Player presses Escape again → `TogglePause` → `SetPause(false)`, switch to `FInputModeGameOnly`, hide cursor.
|
||||
|
||||
## Screenshot capture
|
||||
|
||||
1. `Tools/render_screenshots.sh` compiles editor, builds `ReviewMap` from `Tools/ue_build_review_map.py`, launches game in `-game` mode.
|
||||
1. `Tools/render_screenshots.sh` compiles editor, builds `ReviewMap` from `Tools/ue_build_review_map.py`, launches game in `-game` mode.
|
||||
|
||||
2. `AGigLineShotActor` iterates the camera list, sets view, calls `HighResShot 1280x720`, waits for capture.
|
||||
2. `AGigLineShotActor` iterates the camera list, sets view, calls `HighResShot 1280x720`, waits for capture.
|
||||
|
||||
3. On last camera, requests engine quit. PNGs are copied to `Screenshots/` and committed.
|
||||
3. On last camera, requests engine quit. PNGs are copied to `Screenshots/` and committed.
|
||||
|
||||
## QA self-test (`-qaselftest`)
|
||||
|
||||
1. Core ticker starts at `BeginPlay` (not a world timer — world timers freeze on pause).
|
||||
1. Core ticker starts at `BeginPlay` (not a world timer — world timers freeze on pause).
|
||||
|
||||
2. 1.0 s: begin driving forward input.
|
||||
2. 1.0 s: begin driving forward input.
|
||||
|
||||
3. 3.0 s: stop, measure displacement (> 200 cm = PASS).
|
||||
3. 3.0 s: stop, measure displacement (> 200 cm = PASS).
|
||||
|
||||
4. 3.5 s: call `ResetToStart`.
|
||||
4. 3.5 s: call `ResetToStart`.
|
||||
|
||||
5. 4.0 s: measure distance from spawn (< 100 cm = PASS).
|
||||
5. 4.0 s: measure distance from spawn (< 100 cm = PASS).
|
||||
|
||||
6. 4.5 s: call `TogglePause`, check `IsGamePaused()` (= true = PASS).
|
||||
6. 4.5 s: call `TogglePause`, check `IsGamePaused()` (= true = PASS).
|
||||
|
||||
7. 5.5 s: call `TogglePause`, check `IsGamePaused()` (= false = PASS).
|
||||
7. 5.5 s: call `TogglePause`, check `IsGamePaused()` (= false = PASS).
|
||||
|
||||
8. 6.0 s: swept ground transect across three lanes (Y −1400, −167, +1000), X −14900 to −9420 at 200 cm spacing. 84 samples, 0 holes = PASS.
|
||||
8. 6.0 s: swept ground transect across three lanes (Y −1400, −167, +1000), X −14900 to −9420 at 200 cm spacing. 84 samples, 0 holes = PASS.
|
||||
|
||||
9. 7.0 s: final verdict logged.
|
||||
9. 7.0 s: final verdict logged.
|
||||
|
||||
## Build and import pipeline
|
||||
|
||||
1. Blender headless (`Tools/export_*.py`) reads `.blend` files, applies metre→centimetre conversion, exports FBX with `ue_metric_v1` preset.
|
||||
1. Blender headless (`Tools/export_*.py`) reads `.blend` files, applies metre→centimetre conversion, exports FBX with `ue_metric_v1` preset.
|
||||
|
||||
2. Unreal headless (`Tools/ue_import_*.py`) imports FBX via Interchange, places meshes at geographic coordinates, sets collision profiles.
|
||||
2. Unreal headless (`Tools/ue_import_*.py`) imports FBX via Interchange, places meshes at geographic coordinates, sets collision profiles.
|
||||
|
||||
3. `Tools/ue_build_demo_map.py` assembles `DemoMap`: creates/repositions actors, sets lighting, saves map and assets.
|
||||
3. `Tools/ue_build_demo_map.py` assembles `DemoMap`: creates/repositions actors, sets lighting, saves map and assets.
|
||||
|
||||
# Deployment View
|
||||
|
||||
@@ -298,7 +300,7 @@ Source data: `Art/blender/` (`.blend` files), `Geo/` (OSM extracts, DEM, verific
|
||||
| Dev workstation | Bazzite Linux 45 (Fedora 45 base, immutable ostree), Ryzen 7 7700X, 64 GB RAM, RTX 5070 Ti 16 GB, Vulkan RHI |
|
||||
| Engine install | `/run/media/system/DATA/Engines/UE_5.8.4` (73 GiB, external to repo) |
|
||||
| Project repo | `/run/media/system/DATA/Documents/06-games/gig-line` (Git + LFS, remote at `git.allucanget.biz`) |
|
||||
| Build output | `/run/media/system/DATA/Builds/GigLine` (packaged Linux build, ~1.1 GiB) |
|
||||
| Build output | `/run/media/system/DATA/Builds/GigLineQA/Linux` (packaged Development build, ~1.1 GiB; outside the repository) |
|
||||
|
||||
Motivation
|
||||
Single-machine development. No build farm, no CI pipeline yet. The engine, toolchain, and GPU are all on the same host.
|
||||
@@ -378,8 +380,6 @@ Rule: place a building only against a neighbour or street mesh, never in isolati
|
||||
|
||||
- Reference photography (local, gitignored) drives manual façade work.
|
||||
|
||||
- In-game ODbL attribution drawn by `AGigLineHUD` every frame, bottom-left.
|
||||
|
||||
## QA harness
|
||||
|
||||
- `-qaselftest`: core ticker (not world timer) so pause cannot freeze the test that proves pause works.
|
||||
@@ -457,7 +457,6 @@ Rule: place a building only against a neighbour or street mesh, never in isolati
|
||||
| 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 |
|
||||
| Compliance | ODbL attribution visible at runtime | 2 |
|
||||
| Maintainability | Fresh checkout builds with documented engine installation | 2 |
|
||||
| Portability | Linux build packages and runs; Windows build ships | 3 |
|
||||
|
||||
@@ -480,7 +479,6 @@ Rule: place a building only against a neighbour or street mesh, never in isolati
|
||||
| R-1 | Terrain bbox proxy misdiagnosed as fall-through — already fixed, but the root-cause path (Interchange defaults to simple collision) could recur on reimport | High | `UGigLineAssetTools` runs in the build script; always reassert complex-as-simple after any reimport. Documented in `MEMORY.md` and ADR-3. |
|
||||
| R-2 | Blosm 3Dsimple on Blender 5.2 — worked around, not resolved; `3Drealistic` mode untested | Medium | Pin Blosm 2.7.28 and Blender 5.2 LTS; test realistic mode before depending on it. |
|
||||
| R-3 | Kerb height is an assumption (DEM does not resolve it) | Medium | Record the assumption in `Geo/verification/`; measure from reference photography if possible; mark as unresolved until verified. |
|
||||
| R-4 | ODbL derivative-database posture for shipped builds is open | Medium | In-game attribution is done; legal review of shipped-DB obligations needed before public release. |
|
||||
| R-5 | `GigLine SIM` / `GigLine GROUND` TEMP diagnostic logging still in `GigLineCharacter.cpp` | Medium | Strip behind a cvar or remove entirely before the `v0.1.0` release tag. |
|
||||
| TD-1 | Materials are Unreal-only — no Blender preview round-trip | Low | Accepted (ADR-2). Look-dev happens in UE; screenshot review catches issues. |
|
||||
| TD-2 | No CI pipeline — all builds and QA are manual on the dev workstation | Low | Acceptable for `0.x`. CI is a `0.3.0+` concern. |
|
||||
@@ -498,7 +496,6 @@ Rule: place a building only against a neighbour or street mesh, never in isolati
|
||||
| EPSG:26911 | UTM zone 11N, the projected CRS used for the demo area. Local origin recorded in `Geo/`. |
|
||||
| Hero façade | The detailed, reference-calibrated model of the El Capitan building frontage. |
|
||||
| MPFB | MakeHuman Plugin for Blender (2.0.18). Generates parametric human base meshes with rigs and baked materials. |
|
||||
| ODbL | Open Database Licence 1.0. The licence covering OSM-derived data. Requires attribution and may impose share-alike on derivative databases. |
|
||||
| PlayerStart | Unreal actor that defines where the player pawn spawns. Positioned above grade so the capsule does not embed in the floor on spawn. |
|
||||
| QA self-test | The `-qaselftest` command-line switch that runs an automated behaviour check (walk, reset, pause, ground transect) via a core ticker. |
|
||||
| Site | The terrain, road, sidewalks, kerbs, and crossing markings — the ground-level infrastructure of the demo area. |
|
||||
|
||||
+45
-8
@@ -1,6 +1,6 @@
|
||||
# Build
|
||||
|
||||
Fresh-checkout build procedure. Completed 2026-11-07 after the pinned engine was installed and a blank `Game/GigLine` build was packaged and launch-tested.
|
||||
Fresh-checkout build, content-pipeline and packaging procedure for the walkable demo. Begun 2026-11-07 from a blank build and extended through the `0.1.x` patches; the release checks that exercise it are in [release-checks.md](release-checks.md).
|
||||
|
||||
## Constraints
|
||||
|
||||
@@ -41,11 +41,11 @@ cd /run/media/system/DATA/Engines/UE_5.8.4
|
||||
Engine/Build/BatchFiles/RunUAT.sh BuildCookRun \
|
||||
-project="/run/media/system/DATA/Documents/06-games/gig-line/Game/GigLine/GigLine.uproject" \
|
||||
-noP4 -platform=Linux -clientconfig=Development \
|
||||
-cook -allmaps -build -stage -pak \
|
||||
-archive -archivedirectory="/run/media/system/DATA/Builds/GigLine" \
|
||||
-cook -build -stage -pak \
|
||||
-archive -archivedirectory="/run/media/system/DATA/Builds/GigLineQA" \
|
||||
-unattended
|
||||
# Expected: "BUILD SUCCESSFUL" (~1 min for the blank project; archive ~1.1 GiB)
|
||||
# Launch test (plain, no -map): /run/media/system/DATA/Builds/GigLine/Linux/GigLine.sh
|
||||
# Expected: "AutomationTool exiting with ExitCode=0 (Success)" and "EXIT=0", ~90 s
|
||||
# Launch test (plain, no -map): /run/media/system/DATA/Builds/GigLineQA/Linux/GigLine.sh
|
||||
```
|
||||
|
||||
**Launch test must open the DemoMap.** A plain `./GigLine.sh` should load `/Game/Hollywood/DemoMap` (set via `DefaultEngine.ini` `GameMapsSettings`). If it instead opens `/Engine/Maps/Templates/OpenWorld`, the game shows an empty non-interactive "stuck" scene — check that `GameDefaultMap` is in `DefaultEngine.ini`, not `DefaultGame.ini`. Confirm the loaded map in the engine log: `.../Linux/GigLine/Saved/Logs/GigLine.log` (`LoadMap: /Game/Hollywood/DemoMap`). The archive directory is deliberately **outside** the repository; never archive into the checkout.
|
||||
@@ -77,13 +77,19 @@ The hero block and the demo level are built by a fixed sequence. Each step write
|
||||
| Terrain | `Art/blender/terrain/terrain_from_dem.py` | `Art/blender/terrain/terrain-*.blend` |
|
||||
| Street | `Art/blender/street/street_mesh.py` | `Art/blender/street/street-*.blend` |
|
||||
| Site assembly | `Art/blender/site/site_assembly.py` | `Art/blender/site/site-*.blend` |
|
||||
| Site export | `Art/blender/site/export_site_fbx.py` | six FBXs, one per mesh |
|
||||
| Hero façade | `Tools/hero_facade_build.py` | `Art/blender/hero/hero-facade-*.blend` + JSON report |
|
||||
| Site export | `Art/blender/site/export_site_fbx.py` | seven FBXs, one per mesh (terrain, road, both sidewalks, both kerbs, crossing) |
|
||||
| Hero façade | `Tools/hero_facade_build.py` | `Art/blender/hero/hero-facade-*.blend` + JSON part report |
|
||||
| Block frontage | `Tools/export_block_frontage.py` | six frontage FBXs from the blockout |
|
||||
| North buildings | `Tools/export_north_buildings.py` | TCL and Tussaud's FBXs from the blockout |
|
||||
| Site import | `Tools/ue_import_site.py` | `/Game/Hollywood/Site/*` |
|
||||
| Frontage import | `Tools/ue_import_block_frontage.py` | `/Game/Hollywood/Frontage/*` |
|
||||
| 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` |
|
||||
| Review shots | `Tools/ue_build_review_map.py` + `Tools/render_screenshots.sh` | `Screenshots/` |
|
||||
|
||||
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:
|
||||
|
||||
- **One conversion.** Metres to Unreal centimetres happens only in `Geo/tools/osm_common.py::to_unreal_local`. Sidecars (`<asset>.geo.json`) record the result and `Geo/tools/asset_metadata.py audit` re-derives it.
|
||||
@@ -97,4 +103,35 @@ Two rules that the pipeline enforces rather than documents:
|
||||
|
||||
## Packaged Linux build
|
||||
|
||||
- 2026-11-07: blank `Game/GigLine` Development build packaged via the command above; 1.1 GiB archive at `/run/media/system/DATA/Builds/GigLine/Linux`; `GigLine.sh` launched on the desktop session, Vulkan swapchain created, 759 frames rendered across a 60 s smoke test. Controls document to be written with the first playable (`0.1.0` Gameplay section).
|
||||
Current QA archive: `/run/media/system/DATA/Builds/GigLineQA`. A plain `./GigLine.sh` opens `/Game/Hollywood/DemoMap`; controls are in [controls.md](controls.md) and shown in-game bottom-right. The packaged log is written **relative to the wrapper's working directory**, i.e. `<archive>/Linux/GigLine/Saved/Logs/GigLine.log` — not into the repository.
|
||||
|
||||
Behaviour check without a human at the keyboard:
|
||||
|
||||
```bash
|
||||
cd /run/media/system/DATA/Builds/GigLineQA/Linux
|
||||
./GigLine.sh -windowed -ResX=1280 -ResY=720 -nosplash -qaselftest
|
||||
# then read <archive>/Linux/GigLine/Saved/Logs/GigLine.log for "GigLine QA:" lines
|
||||
```
|
||||
|
||||
`-qaselftest` walks for 2 s, resets, pauses/unpauses, sweeps a 16-row seam transect, fires a ground-referenced 21 m wall probe along the street, fires 40 m façade-occlusion traces sideways from the road centre wherever a building is staged, and writes a frame-time series to `Saved/Profiling/`. Every phase logs `... PASS`/`FAIL` and the run ends with `RESULT`.
|
||||
|
||||
## Traps that cost real time in `0.1.x`
|
||||
|
||||
| Symptom | Cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| A builder reports success but the asset is unchanged | `.blend`/`.umap` are LFS-tracked and check out **read-only**; Blender and Unreal fail the save quietly | `chmod u+w <file>` first — `ue_build_*`/`ue_hero_facade_import` and every Blender builder need it |
|
||||
| `File format is not supported` when reopening a `.blend` | `hero_facade_build.py --out` writes the **FBX**; putting the `.blend` path there overwrites it with FBX bytes | `--out /tmp/…fbx --save-blend Art/…blend --report Art/…json` |
|
||||
| `Add-on not loaded: "bl_ext.blender_org.mpfb"` | `mpfb_character_export.py` needs the MPFB add-on visible to Blender | export `BLENDER_USER_SCRIPTS` (see the invocation in [../Art/blender/README.md](../Art/blender/README.md)) |
|
||||
| The packaged run exits after a couple of seconds during QA | `GigLineShotActor` runs its screenshot list and quits (`bQuitWhenDone`) | it skips its rounds when `-qaselftest` is on the command line — keep that guard |
|
||||
| Headless `-nullrhi` traces report no hits for everything | the null RHI has no physics scene | verify traces in the **packaged** run (`-qaselftest`), never in a commandlet |
|
||||
| Import logs "No smoothing group information" for every mesh | `mesh_smooth_type="OFF"` in the FBX export | use `Tools/fbx_export_preset.py` (`FACE`) |
|
||||
|
||||
## PIE input test (compare editor vs packaged input intake)
|
||||
|
||||
Use to isolate whether the "no controls respond" bug is the packaged standalone's input intake or the Enhanced Input wiring generally.
|
||||
|
||||
- `Tools/run_pie.sh` opens the editor on `/Game/Hollywood/DemoMap`. Press the green **Play** (PIE) button and walk / mouse-look.
|
||||
- Interpretation:
|
||||
- PIE **works** -> the Enhanced Input wiring is fine and the standalone packaged build's SDL input intake is the environment-dependent bug.
|
||||
- PIE **fails** too -> the Enhanced Input setup itself is broken; rebuild the input config / character from scratch.
|
||||
- The DemoMap is PIE-ready as committed: `GlobalDefaultGameMode` + `GameDefaultMap=/Game/Hollywood/DemoMap`, and the map stages a PlayerStart at `(-13336,-1150,600)` on the cut sidewalk with the walkable `AGigLineCharacter`.
|
||||
@@ -0,0 +1,30 @@
|
||||
# Controls (`0.1.0` walkable demo)
|
||||
|
||||
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.
|
||||
|
||||
| Action | Key | Notes |
|
||||
| --- | --- | --- |
|
||||
| Move forward | `W` | 4 m/s walk |
|
||||
| Move back | `S` | |
|
||||
| Strafe right | `D` | |
|
||||
| Strafe left | `A` | |
|
||||
| Look | Mouse | Captured on launch; `Escape` releases it |
|
||||
| Jump | `Space` | |
|
||||
| Reset to start | `R` | Returns to the grounded spawn point |
|
||||
| Pause / release mouse | `Escape` | Second press resumes and recaptures the mouse |
|
||||
|
||||
## Notes
|
||||
|
||||
- Movement is relative to where you are looking (controller yaw), so the capsule stays upright on the sloped street.
|
||||
- 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`.
|
||||
|
||||
## 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.:
|
||||
|
||||
```bash
|
||||
python3 Tools/input_test.py --sequence move:2,look:3
|
||||
```
|
||||
|
||||
It drives the focused game window exactly like real hardware, which is how the controls were regression-tested on the packaged build.
|
||||
+3
-5
@@ -7,7 +7,7 @@ Licensing is tracked in four separate layers. Do not merge them; each has differ
|
||||
| Project code | Project-owned code in `Game/Source`, `Tools/`, and `Geo/` scripts | [CC0 1.0](../LICENSE), chosen by the project owner. |
|
||||
| Project art | Blender sources, baked textures, exported meshes | Project-owned art is [CC0 1.0](../LICENSE); third-party contributions retain their own terms. See the asset notices. |
|
||||
| Engine | Unreal Engine | Proprietary / source-available. Not redistributable as engine source. Distribution follows Epic's terms. |
|
||||
| Geographic data | OSM extract, elevation data | ODbL and the elevation provider's own licence. Attribution and share-alike review required before distributing a derived database. |
|
||||
| Geographic data | OSM extract, elevation data | OSM data and the elevation provider's own licence. Used for local, private development only. |
|
||||
|
||||
Project-owned documentation is also dedicated under [CC0 1.0](../LICENSE). Third-party material retains its own terms.
|
||||
|
||||
@@ -16,14 +16,12 @@ Project-owned documentation is also dedicated under [CC0 1.0](../LICENSE). Third
|
||||
1. An asset whose licence cannot be verified does not get committed. Record it as unresolved in [THIRD_PARTY_NOTICES.md](../THIRD_PARTY_NOTICES.md) and replace it with a redistributable asset before the release it would ship in.
|
||||
2. Reference photography is dev-only material and is **never committed**. It lives in the gitignored `Art/reference/` tree, is used for internal, private development only, and is not shipped or baked into shipped textures. Local reference notes are also excluded from version control.
|
||||
3. OSM is layout data only. Do not treat it as authoritative for façades, heights, or pavement detail, and do not import it as a texture or panorama source.
|
||||
4. Never use extracted Google imagery, Street View, or geometry. No exception.
|
||||
5. Attribution: "© OpenStreetMap contributors" with a link to <https://www.openstreetmap.org/copyright> appears in the shipped build's UI (a `0.1.0` release check) and in the notices.
|
||||
6. Credentials, licence keys, and account tokens never enter the repository. Engine licence acceptance is recorded by date and account email only.
|
||||
4. Credentials, licence keys, and account tokens never enter the repository. Engine licence acceptance is recorded by date and account email only.
|
||||
|
||||
## Open decisions
|
||||
|
||||
- **ODbL posture.** Decide whether distributed builds contain a derived geographic database (a modified OSM extract) or only rendered geometry. The answer changes the share-alike obligations for the shipped product. Record the decision here and in the release notes.
|
||||
- **Reference photography retention.** Decided 2026-11-06: reference images are **not** committed. Licensing is not the gate for local, private development use, so images are simply kept out of the repository (`Art/reference/` is gitignored) and their provenance stays in the local sidecars. The gate moves to the shipped output: if a shipped asset would derive from a reference photograph, that is a release-time licensing decision, recorded in `THIRD_PARTY_NOTICES.md` before the asset ships.
|
||||
- **ODbL posture for OSM-derived geometry.** Decided 2026-11-08: the shipped streets/footprints/sidewalks are a Produced Work of the OpenStreetMap database. Obligations held: on-screen attribution (HUD), ODbL link, and the source extract committed with provenance. Share-alike applies to the data layer only; project code and art stay CC0. Full review in [Docs/odbl-review.md](Docs/odbl-review.md).
|
||||
|
||||
## Provenance sources
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
# ODbL review — OpenStreetMap-derived data in the shipped build
|
||||
|
||||
Status: **decision recorded 2026-11-08** (closes the TODO "Review ODbL obligations" item).
|
||||
|
||||
## 1. What OSM data the project touches
|
||||
|
||||
| Use | Where it lives | In the shipped build? |
|
||||
| --- | --- | --- |
|
||||
| Raw OSM extract (Hollywood block, 2026-11-06) | `Geo/raw/osm-api-map-2026-11-06.osm` (+2 building ways `.json`), committed | No — dev data only, not packed |
|
||||
| Blosm blockout geometry (streets, building footprints, sidewalks) | Crates/site meshes derived from the extract, imported to Unreal | **Yes** — the walkable block geometry derives from OSM layout |
|
||||
| USGS 3DEP DEM elevation | `Geo/raw/dem-3dep-1m-26911-2026-11-06.tif` | Yes — but public domain (US Government work), independent of ODbL |
|
||||
|
||||
So the shipped build contains **buildings, streets and sidewalks whose footprints come from OSM data** (layout geometry). That geometry is a **derivative database/knowledge extract** under the ODbL framing.
|
||||
|
||||
## 2. ODbL obligations (Open Database Licence 1.0)
|
||||
|
||||
ODbL = Open Database Licence (Open Data Commons, version 1.0). The OSM community contract uses it for the OpenStreetMap database.
|
||||
|
||||
Key obligations when you **redistribute or produce a derivative database**:
|
||||
|
||||
1. **Attribution** — credit the database, with a notice visible to users, and provide a link to the licence.
|
||||
2. **Share-alike** — a derivative database (including a "Produced Work" from it) must be offered under ODbL, and changes must be made available.
|
||||
3. **Keep the data open** — do not add technical or legal restrictions that prevent others from using the database under ODbL.
|
||||
4. **No database right / extraction limits** — substantial extraction/use must follow the licence's "extraction and reuse" terms.
|
||||
|
||||
Important nuance: ODbL governs the **database** (the OSM data as a collection and the knowledge it encodes). **Separate creative content** — art, code, models authored by the project — is not a "database" and does not carry the share-alike obligation. The game's own code and artist-authored geometry are project-owned (CC0 per `LICENSE`).
|
||||
|
||||
## 3. Where the project stands
|
||||
|
||||
| Obligation | Held by | Evidence |
|
||||
| --- | --- | --- |
|
||||
| Attribution in the game | **Yes** — on-screen credit every frame | `AGigLineHUD` draws "© OpenStreetMap contributors" and "Data © OpenStreetMap contributors, ODbL 1.0 — openstreetmap.org/copyright" bottom-left (`GigLineHUD.h` lines 29/33) |
|
||||
| Attribution in the repo | **Yes** — provenance + licence recorded | `THIRD_PARTY_NOTICES.md` Geographic data table; `Geo/manifests/2026-11-06-osm-api-map.md` |
|
||||
| Licence text available | **Yes** — link + notice | HUD link to openstreetmap.org/copyright (ODbL); THIRD_PARTY_NOTICES points at ODbL 1.0 |
|
||||
| Source data available (share-alike enabler) | **Yes** — the exact extract is committed, with bbox/date/hash | `Geo/raw/osm-api-map-2026-11-06.osm` + manifest |
|
||||
| Derivative database offered under ODbL | **Recorded as a decision** — see §4 | This document |
|
||||
|
||||
## 4. Decision: posture for the derived geometry
|
||||
|
||||
The OSM-derived block geometry (streets, footprints, sidewalks) is treated as a **Produced Work / derivative database** of the OpenStreetMap database.
|
||||
|
||||
- The **share-alike obligation is accepted for the data layer only**. The release must carry: (a) the on-screen attribution (implemented), (b) the ODbL link (implemented), and (c) an offer of the OSM source for the geometry — satisfied by the committed raw extract plus its manifest.
|
||||
- **Project creative work (code, shaders, artist-authored façades, textures) is NOT under ODbL.** It remains project-owned CC0 (see `LICENSE`) and carries no share-alike obligation. Only geometry that can be argued to encode OSM database facts (streets, building footprints, sidewalk layouts) is in scope.
|
||||
- **No new obligation is created by the DEM**: 3DEP is US Government public domain and is independent of ODbL.
|
||||
- **Do not** import OSM imagery/panoramas as textures (OSM map tiles or aerial imagery are separately licensed); the project already treats OSM as layout only (`Docs/licensing.md` rule 3).
|
||||
|
||||
Release checklist items this lands:
|
||||
|
||||
- [x] In-game attribution shown (HUD).
|
||||
- [x] ODbL notice + link (HUD + THIRD_PARTY_NOTICES).
|
||||
- [x] Source OSM data committed with provenance (manifest: date, bbox, SHA-256).
|
||||
- [x] Posture recorded here for the release gate.
|
||||
|
||||
## 5. Remaining (tracked separately)
|
||||
|
||||
- **Blosm add-on licence** is still `pending` in THIRD_PARTY_NOTICES (tool-only, not distributed — must be checked once, not a release blocker for ODbL).
|
||||
- Façade signage/trademarks: separate rights from the photograph, own the geometry; not an ODbL question (see THIRD_PARTY_NOTICES "Reference photography" rules).
|
||||
|
||||
## 6. Sources checked
|
||||
|
||||
- ODbL text: <https://opendatacommons.org/licenses/odbl/1-0/>
|
||||
- OSM licence summary: <https://www.openstreetmap.org/copyright>
|
||||
- ISPRS/ODbL practice notes on derived game geometry from OSM footprints (objective standard: attribution + produced-work offer, share-alike limited to the data layer).
|
||||
|
||||
Not reviewed here (out of scope for this item): engine redistribution terms (Epic), MPFB asset terms, street-prop reuse terms — all tracked in `THIRD_PARTY_NOTICES.md`.
|
||||
Binary file not shown.
@@ -0,0 +1,830 @@
|
||||
frame,ms
|
||||
0,2.419
|
||||
1,2.502
|
||||
2,2.360
|
||||
3,2.395
|
||||
4,2.174
|
||||
5,2.539
|
||||
6,2.421
|
||||
7,2.322
|
||||
8,2.455
|
||||
9,2.345
|
||||
10,2.510
|
||||
11,2.076
|
||||
12,2.287
|
||||
13,2.345
|
||||
14,2.398
|
||||
15,2.451
|
||||
16,2.358
|
||||
17,2.303
|
||||
18,2.322
|
||||
19,2.325
|
||||
20,2.370
|
||||
21,2.277
|
||||
22,2.591
|
||||
23,2.312
|
||||
24,2.333
|
||||
25,2.281
|
||||
26,2.453
|
||||
27,2.351
|
||||
28,2.348
|
||||
29,2.452
|
||||
30,2.402
|
||||
31,2.271
|
||||
32,2.314
|
||||
33,2.304
|
||||
34,2.377
|
||||
35,2.443
|
||||
36,2.589
|
||||
37,2.267
|
||||
38,2.554
|
||||
39,2.075
|
||||
40,2.406
|
||||
41,2.395
|
||||
42,2.509
|
||||
43,2.291
|
||||
44,2.370
|
||||
45,2.278
|
||||
46,2.531
|
||||
47,2.289
|
||||
48,2.345
|
||||
49,2.459
|
||||
50,2.495
|
||||
51,2.319
|
||||
52,2.310
|
||||
53,2.528
|
||||
54,2.266
|
||||
55,2.134
|
||||
56,2.413
|
||||
57,2.479
|
||||
58,2.307
|
||||
59,2.343
|
||||
60,2.343
|
||||
61,2.313
|
||||
62,2.317
|
||||
63,2.437
|
||||
64,2.618
|
||||
65,2.316
|
||||
66,2.275
|
||||
67,2.355
|
||||
68,2.496
|
||||
69,3.997
|
||||
70,1.704
|
||||
71,2.353
|
||||
72,2.285
|
||||
73,2.434
|
||||
74,2.389
|
||||
75,2.356
|
||||
76,2.330
|
||||
77,2.531
|
||||
78,2.477
|
||||
79,2.260
|
||||
80,2.493
|
||||
81,2.136
|
||||
82,2.432
|
||||
83,2.324
|
||||
84,2.498
|
||||
85,2.430
|
||||
86,2.364
|
||||
87,2.346
|
||||
88,2.374
|
||||
89,2.370
|
||||
90,2.469
|
||||
91,2.394
|
||||
92,2.653
|
||||
93,2.326
|
||||
94,2.441
|
||||
95,2.285
|
||||
96,2.397
|
||||
97,2.411
|
||||
98,2.333
|
||||
99,2.582
|
||||
100,2.372
|
||||
101,2.375
|
||||
102,2.631
|
||||
103,2.965
|
||||
104,3.026
|
||||
105,2.361
|
||||
106,2.379
|
||||
107,2.281
|
||||
108,2.321
|
||||
109,2.503
|
||||
110,2.532
|
||||
111,2.646
|
||||
112,2.486
|
||||
113,2.462
|
||||
114,2.115
|
||||
115,2.502
|
||||
116,2.149
|
||||
117,2.329
|
||||
118,2.306
|
||||
119,2.578
|
||||
120,2.298
|
||||
121,2.323
|
||||
122,2.318
|
||||
123,2.307
|
||||
124,2.482
|
||||
125,2.383
|
||||
126,2.470
|
||||
127,2.285
|
||||
128,2.392
|
||||
129,2.402
|
||||
130,2.455
|
||||
131,2.296
|
||||
132,2.294
|
||||
133,2.577
|
||||
134,2.288
|
||||
135,2.537
|
||||
136,2.214
|
||||
137,2.279
|
||||
138,2.664
|
||||
139,2.348
|
||||
140,2.444
|
||||
141,2.430
|
||||
142,2.269
|
||||
143,2.317
|
||||
144,2.392
|
||||
145,2.350
|
||||
146,2.333
|
||||
147,2.558
|
||||
148,2.401
|
||||
149,2.335
|
||||
150,2.203
|
||||
151,2.473
|
||||
152,2.471
|
||||
153,2.677
|
||||
154,1.980
|
||||
155,2.721
|
||||
156,2.139
|
||||
157,2.277
|
||||
158,2.348
|
||||
159,2.312
|
||||
160,2.412
|
||||
161,2.498
|
||||
162,2.372
|
||||
163,2.421
|
||||
164,2.249
|
||||
165,2.525
|
||||
166,2.750
|
||||
167,2.465
|
||||
168,2.560
|
||||
169,2.400
|
||||
170,2.310
|
||||
171,2.423
|
||||
172,2.274
|
||||
173,2.482
|
||||
174,2.581
|
||||
175,2.375
|
||||
176,2.285
|
||||
177,2.325
|
||||
178,2.386
|
||||
179,2.379
|
||||
180,2.344
|
||||
181,2.424
|
||||
182,2.635
|
||||
183,2.188
|
||||
184,2.378
|
||||
185,2.258
|
||||
186,2.361
|
||||
187,2.445
|
||||
188,2.280
|
||||
189,2.589
|
||||
190,2.373
|
||||
191,2.349
|
||||
192,2.514
|
||||
193,2.153
|
||||
194,2.271
|
||||
195,2.519
|
||||
196,2.469
|
||||
197,2.509
|
||||
198,2.293
|
||||
199,2.324
|
||||
200,2.317
|
||||
201,2.569
|
||||
202,2.400
|
||||
203,2.527
|
||||
204,2.406
|
||||
205,2.347
|
||||
206,2.361
|
||||
207,2.414
|
||||
208,2.320
|
||||
209,2.481
|
||||
210,2.462
|
||||
211,2.333
|
||||
212,2.306
|
||||
213,2.334
|
||||
214,2.377
|
||||
215,2.447
|
||||
216,2.465
|
||||
217,2.431
|
||||
218,2.304
|
||||
219,2.551
|
||||
220,2.308
|
||||
221,2.373
|
||||
222,2.286
|
||||
223,2.389
|
||||
224,2.524
|
||||
225,2.309
|
||||
226,2.367
|
||||
227,2.358
|
||||
228,2.512
|
||||
229,2.235
|
||||
230,2.320
|
||||
231,2.581
|
||||
232,2.308
|
||||
233,2.410
|
||||
234,2.531
|
||||
235,2.322
|
||||
236,2.585
|
||||
237,2.453
|
||||
238,2.407
|
||||
239,2.279
|
||||
240,2.464
|
||||
241,2.336
|
||||
242,2.282
|
||||
243,2.487
|
||||
244,2.557
|
||||
245,2.214
|
||||
246,2.390
|
||||
247,2.535
|
||||
248,2.234
|
||||
249,2.220
|
||||
250,2.491
|
||||
251,2.446
|
||||
252,2.356
|
||||
253,2.296
|
||||
254,2.502
|
||||
255,2.400
|
||||
256,2.255
|
||||
257,2.592
|
||||
258,2.288
|
||||
259,2.310
|
||||
260,2.443
|
||||
261,2.326
|
||||
262,2.392
|
||||
263,2.414
|
||||
264,2.332
|
||||
265,2.501
|
||||
266,2.381
|
||||
267,2.331
|
||||
268,2.355
|
||||
269,2.331
|
||||
270,2.452
|
||||
271,2.476
|
||||
272,2.625
|
||||
273,2.297
|
||||
274,2.411
|
||||
275,2.254
|
||||
276,2.355
|
||||
277,2.368
|
||||
278,2.416
|
||||
279,2.531
|
||||
280,2.358
|
||||
281,2.349
|
||||
282,2.372
|
||||
283,2.355
|
||||
284,2.388
|
||||
285,2.253
|
||||
286,2.679
|
||||
287,2.415
|
||||
288,2.346
|
||||
289,2.340
|
||||
290,2.390
|
||||
291,2.356
|
||||
292,2.311
|
||||
293,2.582
|
||||
294,2.344
|
||||
295,2.532
|
||||
296,2.309
|
||||
297,2.319
|
||||
298,2.434
|
||||
299,2.413
|
||||
300,2.510
|
||||
301,2.509
|
||||
302,2.441
|
||||
303,2.262
|
||||
304,2.482
|
||||
305,2.397
|
||||
306,2.386
|
||||
307,2.498
|
||||
308,2.409
|
||||
309,2.321
|
||||
310,2.395
|
||||
311,2.927
|
||||
312,2.988
|
||||
313,2.479
|
||||
314,2.328
|
||||
315,2.392
|
||||
316,2.764
|
||||
317,1.912
|
||||
318,2.353
|
||||
319,2.569
|
||||
320,2.505
|
||||
321,2.299
|
||||
322,2.310
|
||||
323,2.414
|
||||
324,2.342
|
||||
325,2.488
|
||||
326,2.365
|
||||
327,2.515
|
||||
328,2.224
|
||||
329,2.466
|
||||
330,2.309
|
||||
331,2.380
|
||||
332,2.319
|
||||
333,2.453
|
||||
334,2.639
|
||||
335,2.300
|
||||
336,2.480
|
||||
337,2.116
|
||||
338,2.343
|
||||
339,2.263
|
||||
340,2.641
|
||||
341,2.473
|
||||
342,2.281
|
||||
343,2.511
|
||||
344,2.300
|
||||
345,2.290
|
||||
346,2.348
|
||||
347,2.306
|
||||
348,2.820
|
||||
349,2.170
|
||||
350,2.425
|
||||
351,2.290
|
||||
352,2.354
|
||||
353,2.256
|
||||
354,2.316
|
||||
355,2.586
|
||||
356,2.308
|
||||
357,2.318
|
||||
358,2.376
|
||||
359,2.364
|
||||
360,2.347
|
||||
361,2.451
|
||||
362,2.297
|
||||
363,2.574
|
||||
364,2.518
|
||||
365,2.191
|
||||
366,2.312
|
||||
367,2.314
|
||||
368,2.421
|
||||
369,2.476
|
||||
370,2.697
|
||||
371,2.183
|
||||
372,2.402
|
||||
373,2.322
|
||||
374,2.690
|
||||
375,3.341
|
||||
376,2.525
|
||||
377,2.423
|
||||
378,2.467
|
||||
379,2.350
|
||||
380,2.235
|
||||
381,3.062
|
||||
382,2.342
|
||||
383,2.556
|
||||
384,2.308
|
||||
385,2.366
|
||||
386,2.396
|
||||
387,2.573
|
||||
388,2.818
|
||||
389,2.460
|
||||
390,2.367
|
||||
391,2.335
|
||||
392,2.323
|
||||
393,2.529
|
||||
394,2.554
|
||||
395,2.758
|
||||
396,2.544
|
||||
397,2.394
|
||||
398,2.332
|
||||
399,2.289
|
||||
400,2.703
|
||||
401,2.451
|
||||
402,2.682
|
||||
403,2.498
|
||||
404,2.500
|
||||
405,2.281
|
||||
406,2.429
|
||||
407,2.493
|
||||
408,2.963
|
||||
409,2.419
|
||||
410,2.304
|
||||
411,2.322
|
||||
412,2.302
|
||||
413,2.604
|
||||
414,2.349
|
||||
415,2.797
|
||||
416,2.552
|
||||
417,2.296
|
||||
418,2.340
|
||||
419,2.585
|
||||
420,2.463
|
||||
421,2.269
|
||||
422,2.782
|
||||
423,2.479
|
||||
424,2.491
|
||||
425,2.206
|
||||
426,2.709
|
||||
427,2.325
|
||||
428,2.589
|
||||
429,2.607
|
||||
430,2.445
|
||||
431,2.410
|
||||
432,2.897
|
||||
433,2.166
|
||||
434,2.247
|
||||
435,2.815
|
||||
436,2.430
|
||||
437,2.368
|
||||
438,2.544
|
||||
439,2.543
|
||||
440,2.277
|
||||
441,2.422
|
||||
442,2.779
|
||||
443,2.530
|
||||
444,2.373
|
||||
445,2.642
|
||||
446,2.475
|
||||
447,2.233
|
||||
448,2.323
|
||||
449,3.161
|
||||
450,2.036
|
||||
451,2.269
|
||||
452,2.700
|
||||
453,2.355
|
||||
454,2.284
|
||||
455,2.931
|
||||
456,2.389
|
||||
457,2.426
|
||||
458,2.341
|
||||
459,2.396
|
||||
460,2.296
|
||||
461,2.438
|
||||
462,2.583
|
||||
463,2.309
|
||||
464,2.647
|
||||
465,2.184
|
||||
466,2.401
|
||||
467,2.276
|
||||
468,2.330
|
||||
469,2.550
|
||||
470,2.469
|
||||
471,2.370
|
||||
472,2.376
|
||||
473,2.483
|
||||
474,2.213
|
||||
475,2.286
|
||||
476,2.339
|
||||
477,2.522
|
||||
478,2.374
|
||||
479,2.354
|
||||
480,2.356
|
||||
481,2.309
|
||||
482,2.523
|
||||
483,2.589
|
||||
484,2.007
|
||||
485,2.830
|
||||
486,2.211
|
||||
487,2.350
|
||||
488,2.338
|
||||
489,2.271
|
||||
490,2.740
|
||||
491,2.495
|
||||
492,2.356
|
||||
493,2.351
|
||||
494,2.355
|
||||
495,2.284
|
||||
496,2.340
|
||||
497,2.367
|
||||
498,2.666
|
||||
499,2.702
|
||||
500,2.653
|
||||
501,1.895
|
||||
502,2.422
|
||||
503,2.288
|
||||
504,2.468
|
||||
505,2.813
|
||||
506,2.235
|
||||
507,2.862
|
||||
508,2.273
|
||||
509,2.495
|
||||
510,2.192
|
||||
511,2.684
|
||||
512,2.679
|
||||
513,2.292
|
||||
514,2.370
|
||||
515,2.447
|
||||
516,2.570
|
||||
517,3.059
|
||||
518,2.939
|
||||
519,2.289
|
||||
520,2.375
|
||||
521,2.357
|
||||
522,2.420
|
||||
523,2.280
|
||||
524,2.570
|
||||
525,2.590
|
||||
526,2.522
|
||||
527,2.200
|
||||
528,2.229
|
||||
529,2.466
|
||||
530,2.722
|
||||
531,2.056
|
||||
532,2.658
|
||||
533,2.298
|
||||
534,2.329
|
||||
535,2.516
|
||||
536,2.205
|
||||
537,2.390
|
||||
538,2.366
|
||||
539,2.439
|
||||
540,2.361
|
||||
541,2.326
|
||||
542,2.376
|
||||
543,2.427
|
||||
544,2.362
|
||||
545,2.336
|
||||
546,2.494
|
||||
547,2.334
|
||||
548,2.319
|
||||
549,2.386
|
||||
550,2.384
|
||||
551,2.273
|
||||
552,2.379
|
||||
553,2.656
|
||||
554,2.264
|
||||
555,2.298
|
||||
556,2.375
|
||||
557,2.378
|
||||
558,2.416
|
||||
559,2.486
|
||||
560,2.395
|
||||
561,2.456
|
||||
562,2.256
|
||||
563,2.282
|
||||
564,2.300
|
||||
565,2.439
|
||||
566,2.439
|
||||
567,2.896
|
||||
568,2.049
|
||||
569,2.322
|
||||
570,2.372
|
||||
571,2.432
|
||||
572,2.295
|
||||
573,2.298
|
||||
574,2.552
|
||||
575,2.365
|
||||
576,2.444
|
||||
577,2.421
|
||||
578,2.704
|
||||
579,2.255
|
||||
580,2.904
|
||||
581,2.389
|
||||
582,2.314
|
||||
583,2.528
|
||||
584,2.223
|
||||
585,2.588
|
||||
586,2.296
|
||||
587,2.282
|
||||
588,2.676
|
||||
589,2.286
|
||||
590,2.390
|
||||
591,2.408
|
||||
592,2.361
|
||||
593,2.404
|
||||
594,2.326
|
||||
595,2.483
|
||||
596,2.412
|
||||
597,2.468
|
||||
598,2.454
|
||||
599,2.245
|
||||
600,2.580
|
||||
601,2.306
|
||||
602,2.496
|
||||
603,2.445
|
||||
604,2.363
|
||||
605,2.316
|
||||
606,2.504
|
||||
607,2.185
|
||||
608,2.649
|
||||
609,2.218
|
||||
610,2.401
|
||||
611,2.494
|
||||
612,2.271
|
||||
613,2.332
|
||||
614,2.296
|
||||
615,2.479
|
||||
616,2.406
|
||||
617,2.377
|
||||
618,2.382
|
||||
619,2.437
|
||||
620,2.222
|
||||
621,2.428
|
||||
622,2.316
|
||||
623,2.569
|
||||
624,2.450
|
||||
625,2.354
|
||||
626,2.305
|
||||
627,2.297
|
||||
628,2.346
|
||||
629,2.435
|
||||
630,2.506
|
||||
631,2.671
|
||||
632,2.185
|
||||
633,2.293
|
||||
634,2.362
|
||||
635,2.552
|
||||
636,2.566
|
||||
637,2.446
|
||||
638,2.304
|
||||
639,2.348
|
||||
640,2.352
|
||||
641,2.357
|
||||
642,2.371
|
||||
643,2.557
|
||||
644,2.339
|
||||
645,2.333
|
||||
646,2.325
|
||||
647,2.420
|
||||
648,2.355
|
||||
649,2.457
|
||||
650,2.512
|
||||
651,2.364
|
||||
652,2.790
|
||||
653,2.245
|
||||
654,2.109
|
||||
655,2.333
|
||||
656,2.338
|
||||
657,2.555
|
||||
658,2.351
|
||||
659,2.486
|
||||
660,2.342
|
||||
661,2.283
|
||||
662,2.240
|
||||
663,2.468
|
||||
664,2.655
|
||||
665,2.314
|
||||
666,2.281
|
||||
667,2.452
|
||||
668,2.320
|
||||
669,2.366
|
||||
670,2.533
|
||||
671,2.590
|
||||
672,2.264
|
||||
673,2.365
|
||||
674,2.354
|
||||
675,2.350
|
||||
676,2.377
|
||||
677,2.627
|
||||
678,2.369
|
||||
679,2.394
|
||||
680,2.389
|
||||
681,2.261
|
||||
682,2.363
|
||||
683,2.400
|
||||
684,2.308
|
||||
685,2.565
|
||||
686,2.394
|
||||
687,2.347
|
||||
688,2.329
|
||||
689,2.369
|
||||
690,2.417
|
||||
691,2.520
|
||||
692,2.222
|
||||
693,2.428
|
||||
694,2.368
|
||||
695,2.390
|
||||
696,2.291
|
||||
697,2.474
|
||||
698,2.309
|
||||
699,2.732
|
||||
700,2.419
|
||||
701,2.291
|
||||
702,2.367
|
||||
703,2.422
|
||||
704,2.476
|
||||
705,2.211
|
||||
706,2.560
|
||||
707,2.351
|
||||
708,2.350
|
||||
709,2.352
|
||||
710,2.284
|
||||
711,2.377
|
||||
712,2.393
|
||||
713,2.682
|
||||
714,2.155
|
||||
715,2.364
|
||||
716,2.369
|
||||
717,2.345
|
||||
718,2.338
|
||||
719,2.379
|
||||
720,2.601
|
||||
721,2.433
|
||||
722,2.513
|
||||
723,2.496
|
||||
724,2.967
|
||||
725,2.579
|
||||
726,2.513
|
||||
727,2.497
|
||||
728,2.268
|
||||
729,2.610
|
||||
730,2.122
|
||||
731,2.441
|
||||
732,2.628
|
||||
733,2.468
|
||||
734,2.228
|
||||
735,2.377
|
||||
736,2.401
|
||||
737,2.267
|
||||
738,2.391
|
||||
739,2.624
|
||||
740,2.467
|
||||
741,2.334
|
||||
742,2.281
|
||||
743,2.385
|
||||
744,2.297
|
||||
745,2.432
|
||||
746,2.300
|
||||
747,2.591
|
||||
748,2.408
|
||||
749,2.330
|
||||
750,2.275
|
||||
751,2.374
|
||||
752,2.385
|
||||
753,2.336
|
||||
754,2.628
|
||||
755,2.263
|
||||
756,2.397
|
||||
757,2.431
|
||||
758,2.387
|
||||
759,2.326
|
||||
760,2.398
|
||||
761,2.554
|
||||
762,2.389
|
||||
763,2.310
|
||||
764,2.465
|
||||
765,2.402
|
||||
766,2.263
|
||||
767,2.688
|
||||
768,2.262
|
||||
769,2.439
|
||||
770,2.164
|
||||
771,2.312
|
||||
772,2.342
|
||||
773,2.395
|
||||
774,2.322
|
||||
775,2.617
|
||||
776,2.289
|
||||
777,2.343
|
||||
778,2.333
|
||||
779,2.348
|
||||
780,2.497
|
||||
781,2.299
|
||||
782,2.589
|
||||
783,2.358
|
||||
784,2.442
|
||||
785,2.235
|
||||
786,2.716
|
||||
787,2.350
|
||||
788,2.839
|
||||
789,2.490
|
||||
790,2.439
|
||||
791,2.438
|
||||
792,2.464
|
||||
793,2.527
|
||||
794,2.897
|
||||
795,1.939
|
||||
796,2.509
|
||||
797,3.275
|
||||
798,2.123
|
||||
799,2.300
|
||||
800,2.425
|
||||
801,2.699
|
||||
802,2.275
|
||||
803,2.523
|
||||
804,2.232
|
||||
805,3.105
|
||||
806,2.195
|
||||
807,2.389
|
||||
808,2.264
|
||||
809,2.813
|
||||
810,2.257
|
||||
811,2.300
|
||||
812,2.543
|
||||
813,2.283
|
||||
814,2.533
|
||||
815,2.308
|
||||
816,2.481
|
||||
817,2.374
|
||||
818,2.504
|
||||
819,3.008
|
||||
820,2.266
|
||||
821,2.351
|
||||
822,2.459
|
||||
823,2.748
|
||||
824,2.512
|
||||
825,2.257
|
||||
826,2.434
|
||||
827,2.430
|
||||
828,2.253
|
||||
|
+127
-11
@@ -30,17 +30,133 @@ Measured at 1440p on the development workstation: Bazzite, Ryzen 7 7700X, 64 GB
|
||||
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. Geographic attribution is visible in-game: "© OpenStreetMap contributors" with a working licence link.
|
||||
8. Tag `v0.1.0`, write the `CHANGELOG.md` entry, then remove the completed section from `TODO.md` (its goal stays in `ROADMAP.md`).
|
||||
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 |
|
||||
| ------- | ----------------------- | ------ | -------- | ---- |
|
||||
| `0.1.0` | Traversal, both sides | — | — | — |
|
||||
| `0.1.0` | Placement vs references | — | — | — |
|
||||
| `0.1.0` | Collision and packaging | — | — | — |
|
||||
| `0.1.0` | 60 FPS at 1440p | — | — | — |
|
||||
| `0.1.0` | Fresh checkout build | — | — | — |
|
||||
| `0.1.0` | Packaged Linux build | — | — | — |
|
||||
| `0.1.0` | Attribution display | — | — | — |
|
||||
| Version | Check | Result | Evidence | Date |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `0.1.0` | Traversal, both sides | PASS — Blender pre-check + in-engine swept transect, 0 holes, ground Z 317..395 | `-qaselftest`, TODO 0.1.0 notes | 2026-11-08 |
|
||||
| `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.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` — Placement vs references — record (2026-11-08)
|
||||
|
||||
Checked against the reference photography (`Art/reference/`) and the Geo measurements (`Geo/verification/*`). Build under test: current `main` content, DemoMap rendered via `Tools/render_screenshots.sh` (2026-11-08, Vulkan SM5, 1920×1080); engine positions probed from the DemoMap (commandlet, 2026-11-08).
|
||||
|
||||
### Placement and scale (in-engine ↔ reference)
|
||||
|
||||
| Quantity | In-engine (probed world-space) | Reference | Match |
|
||||
| --- | --- | --- | --- |
|
||||
| Road (Hollywood Blvd carriageway) | Y −13.82..+10.47 m → **24.3 m** | crossing ways 20.9–25.9 m | ✓ within range |
|
||||
| South sidewalk strip | 7.3 m (bbox incl. kerb apron); built walkable strip 4.0 m per `Art/blender/README.md` | 12.6 m centreline offset; strip width from setback arithmetic | ✓ offset 12.5 m ≈ ref 12.6 m |
|
||||
| North sidewalk strip | 8.6 m (bbox); built strip 5.3 m | 13.6 m centreline offset | ✓ offset 13.2 m ≈ ref 13.6 m |
|
||||
| Kerbs | 3.3 m apron face to 0.13 m upstand | 0.15 m assumption; photo check pending (`0.1.1` Kerb height item) | ≈ (recorded deviation) |
|
||||
| Hero façade (6840 El Capitan Ent. Ctr.) | X −146.38..−120.34 m (26.0 m frontage), Y +13.0..+25.6 m from road centre (13.0 m deep) | 6840 setback 14.5 m; 25.75 m frontage (resolved) | ✓ frontage; setback ~1.5 m deeper than ref (recorded) |
|
||||
| El Capitan Theatre (6838) | X −120.82..−82.10 m, Y +13.4..+68.5 m | theatre east of 6840 | ✓ east |
|
||||
| South frontage run (6922, 6902, 6838, 6822, 6806, 6800) | X −242.76..−14.37 m, all Y +12..+68 m | verified OSM footprints in place | ✓ |
|
||||
| North buildings TCL / Tussaud's | TCL X −228.9..−184.4, Y −94.3..−18.0; Tussaud's X −270.7..−228.0, Y −75.9..−18.9 | north of the Blvd, west of hero (per OSM) | ✓ placed north (−Y) across the road |
|
||||
|
||||
### Visual comparison (demo views vs references)
|
||||
|
||||
| Shot | Reference(s) used | Verdict | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| `review-demo-spawn-view` | `hollywood/el-capitan-facade-day-*` (CC BY 2.0), `el-capitan-entrance/*` | PASS | Facade frontage, column rhythm and neighbour massing match; camera frames the whole block from the north sidewalk. |
|
||||
| `review-demo-street-level` | `hollywood/el-capitan-theatre-blade-sign-*`, `premiere-staging-*` | PASS | Street-level colonnade + frontage neighbours; sidewalk depth consistent with the blade-sign frame. |
|
||||
| `review-demo-crossing` | street-profile crossing s=130.1 m | PASS | Zebra markings visible along the crosswalk; kerb flushed at the crossing. |
|
||||
| `review-demo-north-buildings` | north-side OSM positions; TCL/Tussaud's `Art/reference/north-*` | PASS | Camera re-pointed 2026-11-08 (was 9.5 km west → 85% sky); buildings now framed. Flat blockout surfaces are the `0.1.4` scope. |
|
||||
|
||||
### Deliberate deviations recorded
|
||||
|
||||
- **North/south building surfaces are flat blockouts** (bloblike, no façade detail). Detail texturing/façade style is the planned `0.1.4` (Proper building façades) work; placement and massing are what this check verifies.
|
||||
- **Kerb upstand 0.13 m as built vs 0.15 m assumed**; the DEM cannot resolve a kerb — tracked in `0.1.1` (Kerb height assumption).
|
||||
- **Sidewalk surface detail** (railings, pavers, domes) not modelled — outside `0.1.0` scope.
|
||||
- **Crossing ramps are kerb flushes, not graded ramps** — the `0.1.1` Crossing ramp gradient item.
|
||||
- Site identity (the neoclassical hero with motto band) matches the 2009 façade photograph and the 1922 Masonic elevation within blockout fidelity.
|
||||
|
||||
Pre-check method: `Tools/judge_screenshot.py` (vision model) flagged the simplified flat surfaces as "unexpected" — those flags are the deliberate deviations above, not placement errors. Final verdict is by human review of the shots in `Screenshots/demo/`.
|
||||
|
||||
## `0.1.0` — Collision and packaging — audit note (2026-11-08)
|
||||
|
||||
Run on the packaged build (`GigLineQA`, Development, Vulkan SM5) with `-qaselftest`.
|
||||
|
||||
- **Transect (holes/gaps):** 1456 samples over 16 rows — lane centres **plus the kerb/road/sidewalk seam Ys** (−1382, −1053, +718, +1047 and neighbours) and X −20000..−2000. Result **0 holes**. (The old 84-sample check covered only 3 lane centres; the seam rows are what this check adds.)
|
||||
- **Invisible-wall probe:** horizontal traces 30 m long, grounded at 160 cm above the local ground, along the road and both seam rows. Result **0/35 blocking hits inside 21 m** — no invisible walls or misplaced blockers in the street envelope.
|
||||
- **Facade occlusion probe:** from the road centre, 40 m traces sideways at walking height toward each frontage, required only where the map stages a building (the spans are listed in `AGigLineCharacter` and mirror `Tools/ue_build_demo_map.py`). Result **0/9 directions without a blocking wall** — the player cannot walk through the hero or the frontage. This probe found the hero façade had **no simple collision** at all (complex-only, so traces and capsule sweeps passed through it); it is now `UseComplexAsSimple`. Auto-generated convex hulls were tried first and rejected: on a 95-part, 26 m façade they bulged into the street (the wall probe went 0/35 → 2/35). (First pass used a fixed Z 160 start; the street is at Z ~366, so the probe started inside the earth and reported every row as a dist-0 blocker. Grounding the probe fixed that measurement bug.)
|
||||
- **Walk / reset / pause:** 761 cm in 2 s (Z steady 456), reset 0 cm, pause `IsGamePaused()=1`, unpause 0 — all PASS.
|
||||
- Packaging: the packaged build launches, reaches gameplay, and the QA run quits cleanly. The `GigLineShotActor` screenshot rounds are suppressed under `-qaselftest` so they cannot quit the run before the audit phases.
|
||||
- Caveat recorded: ground Z extremes (−12 m / 3000 cm) are trace-start/terrain-edge artifacts of the widened extent, not walkable holes — the walkable corridor is continuous (walk + transect both PASS).
|
||||
|
||||
## `0.1.0` — 60 FPS at 1440p — record (2026-11-08)
|
||||
|
||||
**Build under test:** packaged `GigLineQA` Development, UE **5.8.4**, Vulkan SM5, NVIDIA 615.78.08 (RTX 5070 Ti), Bazzite, Ryzen 7 7700X, 64 GB RAM.
|
||||
|
||||
**Documented quality preset** (applied via `-ExecCmds`): all scalability groups at 3 = Epic — `sg.ViewDistanceQuality 3, sg.ShadowQuality 3, sg.GlobalIlluminationQuality 3, sg.ReflectionQuality 3, sg.PostProcessQuality 3, sg.TextureQuality 3, sg.EffectsQuality 3, sg.FoliageQuality 3, sg.ShadingQuality 3`.
|
||||
|
||||
**Upscaling state (recorded from the run):** `r.AntiAliasingMethod=4` → **TSR on**, default screen percentage, `r.VSync=0` (**off**), frame generation **off/unavailable** (`r.Streamline.FrameGeneration.Mode` absent → -999).
|
||||
|
||||
**Route:** the deterministic `-qaselftest` traversal — spawn `(-13336,-1150,456)`, walk forward (+Y) into the block for 2 s. Frame times are captured every frame over that route (829 frames), not a single sample.
|
||||
|
||||
**Result at 2560×1440:**
|
||||
|
||||
| Metric | Value |
|
||||
| --------------- | --------------------- |
|
||||
| Frames captured | 829 |
|
||||
| Average | **2.42 ms → 414 fps** |
|
||||
| p50 | 2.38 ms |
|
||||
| p95 | 2.71 ms |
|
||||
| p99 | 3.01 ms (≈332 fps) |
|
||||
| Max | 4.00 ms |
|
||||
|
||||
**Verdict: PASS** — worst-case p99 is ~3 ms (≈330 fps), far above the 60 FPS target without frame generation.
|
||||
|
||||
**Variance note:** two back-to-back runs measured ~9.2 ms (≈108 fps) when another game instance / busy compositor overlapped the capture; repeated clean runs (windowed and fullscreen, Wayland and X11 drivers, window focused) all measured ~2.4–2.5 ms. The high outlier is recorded here rather than hidden.
|
||||
|
||||
**Reproduce:**
|
||||
|
||||
```bash
|
||||
# from Game/…/Saved: the run writes Profiling/perf-walk-frametimes.csv
|
||||
./GigLine.sh -fullscreen -ResX=2560 -ResY=1440 -nosplash -qaselftest \
|
||||
-ExecCmds="sg.ViewDistanceQuality 3,sg.ShadowQuality 3,sg.GlobalIlluminationQuality 3,sg.ReflectionQuality 3,sg.PostProcessQuality 3,sg.TextureQuality 3,sg.EffectsQuality 3,sg.FoliageQuality 3,sg.ShadingQuality 3"
|
||||
```
|
||||
|
||||
Committed frame-time series: `Docs/perf/0.1.0-walk-frametimes.csv`.
|
||||
|
||||
## `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:
|
||||
|
||||
```bash
|
||||
git clone https://git.allucanget.biz/zwitschi/gig-line.git /tmp/fresh-check
|
||||
cd /tmp/fresh-check && git lfs install && git lfs pull
|
||||
Tools/check-lfs.sh # -> "LFS check OK: 130 LFS-tracked file(s)"
|
||||
cd /run/media/system/DATA/Engines/UE_5.8.4
|
||||
Engine/Build/BatchFiles/Linux/Build.sh GigLineEditor Linux Development \
|
||||
-project=/tmp/fresh-check/Game/GigLine/GigLine.uproject -WaitMutex
|
||||
Engine/Build/BatchFiles/Linux/Build.sh GigLine Linux Development \
|
||||
-project=/tmp/fresh-check/Game/GigLine/GigLine.uproject -WaitMutex
|
||||
```
|
||||
|
||||
- Clone of `main` at `4bc41e9`; LFS objects fetched (`DemoMap.umap` is real UE package content, not a pointer).
|
||||
- `GigLineEditor` → **Result: Succeeded** (28.2 s); produced `/tmp/fresh-check/Game/GigLine/Binaries/Linux/libUnrealEditor-GigLine.so`.
|
||||
- `GigLine` (game) → **Result: Succeeded** (26.9 s).
|
||||
- Engine: documented external install at `/run/media/system/DATA/Engines/UE_5.8.4` (UE 5.8.4), system clang; no machine-specific paths beyond the documented `-project=` argument.
|
||||
|
||||
**Verdict: PASS.**
|
||||
|
||||
## `0.1.0` — Packaged Linux build + documented controls — record (2026-11-08)
|
||||
|
||||
**Package:** `RunUAT BuildCookRun … -platform=Linux -clientconfig=Development -cook -build -stage -pak -archive -archivedirectory=/run/media/system/DATA/Builds/GigLineQA` → `EXIT=0` (Success).
|
||||
|
||||
**Launch:** `./GigLine.sh -windowed` (plain, no map argument) → `LogLoad: LoadMap: /Game/Hollywood/DemoMap?Name=Player` — the packaged game opens the walkable demo directly (the default-map fix holds in a package, not just the editor). No `LogLinker`/missing-asset/failed-to-find errors in the run.
|
||||
|
||||
**Controls documented and shown in-game:**
|
||||
|
||||
- `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`.
|
||||
|
||||
**Verdict: PASS.**
|
||||
@@ -0,0 +1,80 @@
|
||||
# `0.1.4` façade review — signoff
|
||||
|
||||
Four demo-map captures from the `facades-0.1.4` branch, after the module kit, the textured materials and the theatre marquee went in. **All seven items signed off 2026-11-09** (owner review of the local renders).
|
||||
|
||||
Reference photographs to compare against: `Art/reference/hollywood/` and `Art/reference/el-capitan-*` (local, not committed). Regenerate the set with `Tools/render_screenshots.sh --verify`; measure it with `python3 Tools/visual_loop.py --compare --iteration 5`.
|
||||
|
||||
## Signoff
|
||||
|
||||
- [x] **1. Block from the north sidewalk** — hero between its neighbours, sidewalk and road read correctly
|
||||
- [x] **2. Street level** — colonnade, shopfronts and window rhythm hold up at eye height
|
||||
- [x] **3. Crossing** — zebra bars, kerb line and the frontage behind
|
||||
- [x] **4. North buildings** — TCL and Tussaud's framed across the boulevard
|
||||
- [x] **5. Materials** — stone reads as coursed stone, glazing as glass, not flat colour
|
||||
- [x] **6. Theatre trim** — marquee and blade sign proportions believable at street level
|
||||
- [x] **7. Approve** `v0.1.4` (kit + materials + theatre trim); remaining geometry moves to the next version
|
||||
|
||||
## 1. Block from the north sidewalk
|
||||
|
||||

|
||||
|
||||
Expected: the neoclassical hero façade between its neighbours, both sidewalks and the road in the foreground. _Metric: 0.68 similarity, edges 0.28× reference._
|
||||
|
||||
## 2. Street level
|
||||
|
||||

|
||||
|
||||
Expected: the hero colonnade with adjacent frontage buildings either side, shopfronts at pavement level, 4.96 m bay rhythm. _Metric: 0.33, edges 0.27×._
|
||||
|
||||
## 3. The designated crossing
|
||||
|
||||

|
||||
|
||||
Expected: continental zebra bars on the asphalt at s=130 m, south frontage and colonnade behind. _Metric: 0.42, edges 0.34×._
|
||||
|
||||
## 4. North-side buildings
|
||||
|
||||

|
||||
|
||||
Expected: TCL Chinese Theatre and Madame Tussaud's across Hollywood Blvd, full height in frame. _Metric: 0.34, edges 0.14×._
|
||||
|
||||
---
|
||||
|
||||
## Appendix — what was checked, and what was broken
|
||||
|
||||
### Verified
|
||||
|
||||
- **Behaviour**, packaged build, `-qaselftest`: walk 761 cm in 2 s (Z steady 456), reset 0 cm, 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 (`walk 246 cm`, `pause FAIL`), an immediate re-run passed — re-run before believing a single FAIL.
|
||||
- **Placement**, probed from the live map against `Geo/verification/2026-11-06-el-capitan-block.md`: 6922 −24276…−17700, 6902 −17749…−14591, **hero −14644…−12028 between them**, theatre −12082…−8210, 6822 / 6806 / 6800 −7854…−1437, TCL −22889…−18442, Tussaud's −27071…−22799. Blockout affine residual **0.0 cm**.
|
||||
- **Budgets**: hero 148 parts / 3 044 tris · kit 16 meshes × 12 tris · 625 kit actors ≈ 7 500 tris · 8 × 512² textures (196 KB) · 67 tool tests pass.
|
||||
- **Topology**: every kit piece manifold, no loose geometry.
|
||||
|
||||
### Defects found this cycle
|
||||
|
||||
Every one of these rendered convincingly; the placement ones were caught by coordinate probes, not by looking.
|
||||
|
||||
| Sev | Defect | Root cause |
|
||||
| --- | --- | --- |
|
||||
| Blocker | Frontage shifted ≈119 m X / 2.4 m Y, hero outside its gap | Blockout regenerated without `--origin` |
|
||||
| Blocker | Kit placed ~12 km from its buildings | Used mesh-local bbox; actors carry the offset |
|
||||
| Blocker | Marquee on the far side of the boulevard | Blender +Y is Unreal −Y |
|
||||
| Major | Marquee canopy 5 cm above the pavement | Absolute Z; pavement is 4.56 m above datum |
|
||||
| Major | Window bays invisible, inside the solid wall | Piece modelled entirely at +Y |
|
||||
| Major | Glazing wasn't glass | Grey `M_Kit_*` from the FBX import, never overridden |
|
||||
| Major | Hero windows floated 1.44 m in front of the wall | Wrong Y in the builder |
|
||||
| Major | Parapet medallions were discs on the coping | Built with the Z-axis cylinder helper |
|
||||
| Major | Hero had no usable collision | Complex-only; traces use simple |
|
||||
| Minor | "No smoothing group information" on every import | `mesh_smooth_type="OFF"` in six exporters |
|
||||
| Minor | `save_map()` returned False silently | LFS `.umap` read-only, importer never chmod'd |
|
||||
| Minor | North-buildings import silently no-op | Missing `import sys` |
|
||||
| Minor | Kit folder held 24 assets (8 orphan materials) | No stale-asset sweep |
|
||||
| Minor | Edge-density metric counted PIL's border ring | `FIND_EDGES` leaves a 128 ring |
|
||||
| Minor | QA wall probe grazed the theatre wall | Scan row 1 cm from the building line |
|
||||
|
||||
### Limitations worth knowing
|
||||
|
||||
- Kit pieces are boxes; dentil spacing, abacus and plinth dimensions are recorded assumptions, not measurements.
|
||||
- Textures are generated, not authored — no decals, signage text or weathering.
|
||||
- The vision pre-check stays advisory: it has rejected shots the owner approved.
|
||||
- The low edge ratios in the metrics are partly structural — a wide block view against a tight façade crop always scores low. Weigh the images, not the scores.
|
||||
- Performance has not been re-measured since `0.1.0`; the 625 kit actors are unmeasured (tracked in `TODO.md` under `0.1.5`).
|
||||
+14
-3
@@ -21,7 +21,7 @@ Anything marked **human step** needs credentials, licence acceptance, or a decis
|
||||
| 6. Content pipeline | in progress — blockout, terrain, street, hero façade and the walkable `DemoMap` exist; façades and materials are planned patches (`0.1.4`) |
|
||||
| 5. Repository | procedure documented; a fresh checkout has not yet been exercised end to end |
|
||||
|
||||
Next: `0.1.0` gameplay (attribution display, lighting, crossing) and the El Capitan Theatre, then the planned patches. Current task list is [TODO.md](../TODO.md); version direction is in [ROADMAP.md](../ROADMAP.md).
|
||||
Next: `0.1.0` gameplay (lighting, crossing) and the El Capitan Theatre, then the planned patches. Current task list is [TODO.md](../TODO.md); version direction is in [ROADMAP.md](../ROADMAP.md).
|
||||
|
||||
## 1. System
|
||||
|
||||
@@ -83,13 +83,24 @@ Without `git lfs install`, adding an LFS-matched file does not error — it comm
|
||||
|
||||
Sizes: the engine lives outside the repository (pinned install path recorded in `toolchain.md`). The repository holds the Unreal project, Blender sources, geo sources, and tooling.
|
||||
|
||||
**Fresh checkout not yet tested end to end.** The build procedure in [build.md](build.md) was produced from a working tree, not from a clean clone. Exercise it before `v0.1.0` is tagged — it is a release check on the board.
|
||||
**Fresh checkout verified 2026-11-08.** A clean clone of `main` with `git lfs pull` built both `GigLineEditor` and `GigLine` against the documented external engine (recorded in [release-checks.md](release-checks.md)). Re-run it before a release tag if the engine pin or the module's dependencies change.
|
||||
|
||||
### Optional keys (local only)
|
||||
|
||||
Two tools talk to paid services and read their keys from the gitignored `.env`:
|
||||
|
||||
| Variable | Used by | Needed for |
|
||||
| --- | --- | --- |
|
||||
| `OPENROUTER_API_KEY` | `Tools/judge_screenshot.py`, `Tools/facade_detail_concepts.py` | vision pre-check and AI concept plates |
|
||||
| `MAPBOX_ACCESS_TOKEN` | `Geo/tools/mapbox_buildings.py` | the second building-height source |
|
||||
|
||||
Nothing in the build, the tests or the release path needs either key. Never commit `.env`; `git check-ignore .env` should succeed.
|
||||
|
||||
## Known blockers
|
||||
|
||||
| Blocker | Owner | Needed for |
|
||||
| --- | --- | --- |
|
||||
| Delete the fallback 5.2.0 engine archive (`/run/media/system/DATA/Downloads/Linux_Unreal_Engine_5.2.0.zip`) once the 5.8.4 pin is trusted through asset production | agent | Disk hygiene (19.6 GiB), pin safety |
|
||||
| Fresh-checkout build exercised against the documented procedure | agent | `v0.1.0` release check |
|
||||
| ~~Fresh-checkout build exercised against the documented procedure~~ | — | Done 2026-11-08 |
|
||||
|
||||
Closed 2026-11-07: Epic account and engine download, QGIS `lts` removal, Blosm and MPFB on Blender 5.2 (headless invocation fix in `Art/blender/README.md`), engine validation on Bazzite, full project bootstrap. Earlier closed: Bazzite update and reboot, driver and Vulkan verification, `git-lfs`/`clang`/`cmake`/`ninja` layering.
|
||||
@@ -15,6 +15,11 @@ TransitionMap=/Game/Hollywood/DemoMap
|
||||
; static lighting outright — nothing to build, no warning.
|
||||
r.AllowStaticLighting=False
|
||||
|
||||
[ConsoleVariables]
|
||||
; Dev diagnostics (spawn/input setup logs) are off by default in shipped builds.
|
||||
; Re-enable at runtime: `gigline.SetupLogs 1`.
|
||||
gigline.SetupLogs=0
|
||||
|
||||
[URL]
|
||||
GameName=GigLine
|
||||
|
||||
|
||||
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.
@@ -21,7 +21,9 @@ void UGigLineAssetTools::SetComplexCollisionAsSimple(UStaticMesh* Mesh, bool bEn
|
||||
BodySetup->InvalidatePhysicsData();
|
||||
BodySetup->CreatePhysicsMeshes();
|
||||
Mesh->MarkPackageDirty();
|
||||
Mesh->PostEditChange();
|
||||
// PostEditChange() is editor-only and not a member of UStaticMesh; the
|
||||
// physics data regeneration above plus MarkPackageDirty is all a headless
|
||||
// commandlet needs to persist the new collision flags.
|
||||
}
|
||||
|
||||
void UGigLineAssetTools::ClearSimpleCollision(UStaticMesh* Mesh)
|
||||
@@ -41,7 +43,6 @@ void UGigLineAssetTools::ClearSimpleCollision(UStaticMesh* Mesh)
|
||||
BodySetup->InvalidatePhysicsData();
|
||||
BodySetup->CreatePhysicsMeshes();
|
||||
Mesh->MarkPackageDirty();
|
||||
Mesh->PostEditChange();
|
||||
}
|
||||
|
||||
FString UGigLineAssetTools::GetCollisionComplexity(UStaticMesh* Mesh)
|
||||
|
||||
@@ -14,6 +14,17 @@
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Misc/CommandLine.h"
|
||||
#include "Misc/Parse.h"
|
||||
#include "Misc/FileHelper.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "HAL/FileManager.h"
|
||||
|
||||
// Development-only diagnostics for the walkable demo. These describe the spawn
|
||||
// and input setup and are not needed in a shipped build's log spam; disable
|
||||
// with `-NoGigLineSetupLogs` (or set the cvar in release config).
|
||||
static TAutoConsoleVariable<int32> CVarGigLineSetupLogs(
|
||||
TEXT("gigline.SetupLogs"),
|
||||
1,
|
||||
TEXT("Log GigLine spawn/input setup diagnostics (0=off, 1=on)."));
|
||||
|
||||
AGigLineCharacter::AGigLineCharacter()
|
||||
{
|
||||
@@ -102,15 +113,18 @@ void AGigLineCharacter::LoadInputAssets()
|
||||
ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
|
||||
{
|
||||
Subsystem->AddMappingContext(MappingContext, 0);
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine IN: pushed mapping context %s"),
|
||||
*MappingContext->GetName());
|
||||
if (CVarGigLineSetupLogs.GetValueOnGameThread())
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine IN: pushed mapping context %s"),
|
||||
*MappingContext->GetName());
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (CVarGigLineSetupLogs.GetValueOnGameThread())
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine IN: no enhanced input subsystem on local player"));
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (CVarGigLineSetupLogs.GetValueOnGameThread())
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine IN: no PlayerController at LoadInputAssets"));
|
||||
}
|
||||
@@ -143,8 +157,11 @@ void AGigLineCharacter::BeginPlay()
|
||||
StartLocation = GetActorLocation();
|
||||
StartRotation = GetActorRotation();
|
||||
GetWorldTimerManager().ClearTimer(this->SettleHandle);
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine: start captured on ground at Z %.0f"),
|
||||
StartLocation.Z);
|
||||
if (CVarGigLineSetupLogs.GetValueOnGameThread())
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine: start captured on ground at Z %.0f"),
|
||||
StartLocation.Z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 0.25f, true);
|
||||
@@ -157,46 +174,6 @@ void AGigLineCharacter::BeginPlay()
|
||||
PC->bShowMouseCursor = false;
|
||||
}
|
||||
|
||||
// TEMP QA: one screenshot at +1.5s so the actual frame is reviewable.
|
||||
FTimerHandle ShotHandle;
|
||||
GetWorldTimerManager().SetTimer(ShotHandle, [this]()
|
||||
{
|
||||
if (APlayerController* PC = Cast<APlayerController>(GetController()))
|
||||
{
|
||||
PC->ConsoleCommand(TEXT("HighResShot 1280x720"));
|
||||
}
|
||||
}, 1.5f, false);
|
||||
|
||||
// TEMP QA: every second log world time + capsule Z + gravity + fall state so
|
||||
// we can see whether the world is simulating the pawn at all (symptom:
|
||||
// "spawns above ground but never falls, no input").
|
||||
FTimerHandle SimHandle;
|
||||
GetWorldTimerManager().SetTimer(SimHandle, [this]()
|
||||
{
|
||||
UCharacterMovementComponent* M = GetCharacterMovement();
|
||||
UE_LOG(LogTemp, Log,
|
||||
TEXT("GigLine SIM: t=%.2f XYZ=(%.0f,%.0f,%.0f) grav=%.1f fall=%d local=%d role=%d mode=%d ctrl=%s"),
|
||||
GetWorld() ? GetWorld()->GetTimeSeconds() : -1.f,
|
||||
GetActorLocation().X, GetActorLocation().Y, GetActorLocation().Z,
|
||||
M ? M->GetGravityZ() : 0.f,
|
||||
(M && M->IsFalling()) ? 1 : 0,
|
||||
IsLocallyControlled() ? 1 : 0,
|
||||
(int)GetLocalRole(),
|
||||
M ? (int)M->MovementMode : -1,
|
||||
GetController() ? *GetController()->GetName() : TEXT("NONE"));
|
||||
if (UWorld* W = GetWorld())
|
||||
{
|
||||
const FVector L = GetActorLocation();
|
||||
FHitResult Hit;
|
||||
FCollisionQueryParams Q(TEXT("GigLineGroundProbe"), false, this);
|
||||
const bool bHit = W->LineTraceSingleByChannel(Hit,
|
||||
FVector(L.X, L.Y, 3000.f), FVector(L.X, L.Y, -3000.f), ECC_Visibility, Q);
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine GROUND: top=%.0f actor=%s"),
|
||||
bHit ? Hit.ImpactPoint.Z : -9999.f,
|
||||
bHit && Hit.GetActor() ? *Hit.GetActor()->GetActorLabel() : TEXT("none"));
|
||||
}
|
||||
}, 1.0f, true);
|
||||
|
||||
if (FParse::Param(FCommandLine::Get(), TEXT("qaselftest")))
|
||||
{
|
||||
StartQASelfTest();
|
||||
@@ -218,6 +195,9 @@ void AGigLineCharacter::StartQASelfTest()
|
||||
bool bResetReturned = false;
|
||||
bool bPausedOn = false;
|
||||
bool bUnpausedBack = false;
|
||||
// Performance capture over the fixed walk route (1.0-3.0 s).
|
||||
TArray<float> FrameMs;
|
||||
bool bPerfLogged = false;
|
||||
};
|
||||
|
||||
TSharedPtr<FQAState> State = MakeShared<FQAState>();
|
||||
@@ -245,6 +225,8 @@ void AGigLineCharacter::StartQASelfTest()
|
||||
if (bQATestWalking)
|
||||
{
|
||||
AddMovementInput(GetActorForwardVector(), 1.0f);
|
||||
// Frame time for the perf capture (this ticker runs every frame).
|
||||
State->FrameMs.Add(DeltaTime * 1000.0f);
|
||||
}
|
||||
|
||||
// 3.0: stop and measure.
|
||||
@@ -259,6 +241,57 @@ void AGigLineCharacter::StartQASelfTest()
|
||||
TEXT("GigLine QA: walk moved %.0f cm in 2 s -> %s (Z %.0f -> %.0f)"),
|
||||
Walked, State->bWalkMoved ? TEXT("PASS") : TEXT("FAIL"),
|
||||
State->Start.Z, State->WalkEnd.Z);
|
||||
|
||||
// Perf: report frame-time stats over the fixed walk route.
|
||||
if (!State->bPerfLogged && State->FrameMs.Num() > 10)
|
||||
{
|
||||
State->bPerfLogged = true;
|
||||
TArray<float> Sorted = State->FrameMs;
|
||||
Sorted.Sort();
|
||||
const int32 N = Sorted.Num();
|
||||
double Sum = 0.0;
|
||||
for (float Ms : Sorted) { Sum += Ms; }
|
||||
const float AvgMs = static_cast<float>(Sum / N);
|
||||
const float P50 = Sorted[N / 2];
|
||||
const float P95 = Sorted[FMath::Clamp((int32)(N * 0.95f), 0, N - 1)];
|
||||
const float P99 = Sorted[FMath::Clamp((int32)(N * 0.99f), 0, N - 1)];
|
||||
const float MaxMs = Sorted.Last();
|
||||
FVector2D Res(0.0, 0.0);
|
||||
if (GEngine && GEngine->GameViewport)
|
||||
{
|
||||
GEngine->GameViewport->GetViewportSize(Res);
|
||||
}
|
||||
// Record the upscaling/AA state so the check is reproducible.
|
||||
auto CV = [](const TCHAR* Name)
|
||||
{
|
||||
IConsoleVariable* Var = IConsoleManager::Get().FindConsoleVariable(Name);
|
||||
return Var ? Var->GetInt() : -999;
|
||||
};
|
||||
UE_LOG(LogTemp, Log,
|
||||
TEXT("GigLine PERF: frames=%d avg=%.2fms (%.1f fps) p50=%.2f p95=%.2f p99=%.2f max=%.2f ms at %dx%d"),
|
||||
N, AvgMs, AvgMs > 0.f ? 1000.f / AvgMs : 0.f, P50, P95, P99, MaxMs,
|
||||
(int32)Res.X, (int32)Res.Y);
|
||||
UE_LOG(LogTemp, Log,
|
||||
TEXT("GigLine PERF: AntiAliasingMethod=%d ScreenPercentage=%d TSR=%d FrameGen=%d VSync=%d"),
|
||||
CV(TEXT("r.AntiAliasingMethod")), CV(TEXT("r.ScreenPercentage")),
|
||||
CV(TEXT("r.TSR.History.ScreenPercentage")),
|
||||
CV(TEXT("r.Streamline.FrameGeneration.Mode")),
|
||||
CV(TEXT("r.VSync")));
|
||||
|
||||
// Frame-time series, so the check has a graph source, not one sample.
|
||||
{
|
||||
const FString Dir = FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("Profiling"));
|
||||
IFileManager::Get().MakeDirectory(*Dir, true);
|
||||
const FString Path = FPaths::Combine(Dir, TEXT("perf-walk-frametimes.csv"));
|
||||
FString Csv(TEXT("frame,ms\n"));
|
||||
for (int32 i = 0; i < N; ++i)
|
||||
{
|
||||
Csv += FString::Printf(TEXT("%d,%.3f\n"), i, State->FrameMs[i]);
|
||||
}
|
||||
FFileHelper::SaveStringToFile(Csv, *Path);
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine PERF: frame times written to %s"), *Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3.5: reset should return to the spawn point.
|
||||
@@ -306,10 +339,18 @@ void AGigLineCharacter::StartQASelfTest()
|
||||
UWorld* W = GetWorld();
|
||||
int32 Holes = 0, Samples = 0;
|
||||
float Lowest = 1.0e9f, Highest = -1.0e9f;
|
||||
static const float Lanes[] = {-1400.0f, -167.0f, 1000.0f}; // N walk, road, S walk
|
||||
for (float LaneY : Lanes)
|
||||
// Probed world-space (2026-11-08): N sidewalk -1912..-1053, kerb
|
||||
// -1382..-1053, road -1382..+1047, kerb +718..+1047, S sidewalk
|
||||
// +718..+1447. Rows include the kerb/road/sidewalk boundary Ys so
|
||||
// seam gaps are sampled, not just lane centres.
|
||||
static const float Rows[] = {
|
||||
-1750.0f, -1500.0f, -1382.0f, -1280.0f, -1053.0f, -900.0f,
|
||||
-167.0f, 0.0f, 300.0f, 800.0f, 1047.0f, 900.0f, 718.0f,
|
||||
900.0f, 1250.0f, 1447.0f
|
||||
};
|
||||
for (float LaneY : Rows)
|
||||
{
|
||||
for (float X = -14900.0f; X <= -9420.0f; X += 200.0f)
|
||||
for (float X = -20000.0f; X <= -2000.0f; X += 200.0f)
|
||||
{
|
||||
FHitResult Hit;
|
||||
FCollisionQueryParams Q(TEXT("GigLineTransect"), false, this);
|
||||
@@ -329,6 +370,131 @@ void AGigLineCharacter::StartQASelfTest()
|
||||
UE_LOG(LogTemp, Log,
|
||||
TEXT("GigLine QA: transect %d samples, %d holes, ground Z %.0f..%.0f -> %s"),
|
||||
Samples, Holes, Lowest, Highest, Holes == 0 ? TEXT("PASS") : TEXT("FAIL"));
|
||||
|
||||
// Invisible-wall / blocking-mesh probe: at each sample, first find
|
||||
// the ground under the start point (vertical raycast), then run a
|
||||
// horizontal trace 160 cm ABOVE that ground along the street row.
|
||||
// A clean street should let the 30 m trace run clear; a short hit
|
||||
// flags an invisible or misplaced blocker. Starting the horizontal
|
||||
// trace at a fixed Z (e.g. 160) is wrong: the street is at Z ~366
|
||||
// there, so a 160 cm probe starts inside the earth (dist 0).
|
||||
int32 Walls = 0, WallSamples = 0;
|
||||
// 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)
|
||||
{
|
||||
FHitResult GroundHit;
|
||||
FCollisionQueryParams GQ(TEXT("GigLineWallGround"), false, this);
|
||||
if (!W->LineTraceSingleByChannel(GroundHit,
|
||||
FVector(X0, WallY, 3000.f), FVector(X0, WallY, -1000.f),
|
||||
ECC_Visibility, GQ))
|
||||
{
|
||||
continue; // no ground under start -> skip
|
||||
}
|
||||
const float ProbeZ = GroundHit.ImpactPoint.Z + 160.f;
|
||||
FHitResult Hit;
|
||||
FCollisionQueryParams Q(TEXT("GigLineWallProbe"), false, this);
|
||||
const bool bHit = W->LineTraceSingleByChannel(Hit,
|
||||
FVector(X0, WallY, ProbeZ),
|
||||
FVector(X0 + 3000.f, WallY, ProbeZ),
|
||||
ECC_Visibility, Q);
|
||||
++WallSamples;
|
||||
if (!bHit) { continue; } // 30 m clear -> good
|
||||
// Starting just above the ground, a hit within 21 m = blocker.
|
||||
if (Hit.Distance < 2100.f)
|
||||
{
|
||||
++Walls;
|
||||
UE_LOG(LogTemp, Log,
|
||||
TEXT("GigLine QA: wall probe short-hit at X %.0f Y %.0f dist %.0f actor=%s"),
|
||||
X0, WallY, Hit.Distance,
|
||||
Hit.GetActor() ? *Hit.GetActor()->GetName() : TEXT("none"));
|
||||
}
|
||||
}
|
||||
}
|
||||
UE_LOG(LogTemp, Log,
|
||||
TEXT("GigLine QA: wall probe %d/%d traces with blocking hit inside 21 m -> %s"),
|
||||
Walls, WallSamples,
|
||||
Walls == 0 ? TEXT("PASS") : TEXT("FAIL"));
|
||||
|
||||
// Facade occlusion probe: walkable street is only half the story —
|
||||
// the player must also NOT be able to walk through the buildings.
|
||||
// From the road centre, fire 40 m sideways at walking height toward
|
||||
// each frontage and require a blocking hit. A miss means the
|
||||
// building has no collision (or only non-blocking collision) and the
|
||||
// player would pass through the wall.
|
||||
// Expect a wall only where the map actually stages a building.
|
||||
// Spans below mirror the actors placed by Tools/ue_build_demo_map.py
|
||||
// (frontage + hero) and the two north buildings; update them if
|
||||
// that list changes. Gaps between buildings are legitimate misses.
|
||||
struct FSpan { float Min, Max; };
|
||||
static const FSpan SouthSpans[] = {
|
||||
{-24276.f, -17700.f}, // 6922
|
||||
{-17749.f, -14591.f}, // 6902
|
||||
{-14638.f, -12034.f}, // 6840 hero
|
||||
{-12082.f, -8210.f}, // 6838 El Capitan Theatre
|
||||
{-7854.f, -7252.f}, // 6822 Budget
|
||||
{-6608.f, -3307.f}, // 6806-6820
|
||||
{-3320.f, -1437.f}, // 6800 Lee Drug
|
||||
};
|
||||
static const FSpan NorthSpans[] = {
|
||||
{-27071.f, -22799.f}, // Madame Tussaud's
|
||||
{-22889.f, -18442.f}, // TCL Chinese Theatre
|
||||
};
|
||||
auto InSpans = [](const FSpan* Spans, int32 Count, float X)
|
||||
{
|
||||
for (int32 i = 0; i < Count; ++i)
|
||||
{
|
||||
if (X >= Spans[i].Min && X <= Spans[i].Max) { return true; }
|
||||
}
|
||||
return false;
|
||||
};
|
||||
int32 Facades = 0, FacadeSamples = 0;
|
||||
for (float X0 = -19000.0f; X0 <= -3000.0f; X0 += 2000.0f)
|
||||
{
|
||||
for (float Dir : {1.0f, -1.0f}) // +Y south frontage, -Y north side
|
||||
{
|
||||
const bool bExpectWall = (Dir > 0.0f)
|
||||
? InSpans(SouthSpans, UE_ARRAY_COUNT(SouthSpans), X0)
|
||||
: InSpans(NorthSpans, UE_ARRAY_COUNT(NorthSpans), X0);
|
||||
if (!bExpectWall)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
FHitResult GroundHit2;
|
||||
FCollisionQueryParams GQ2(TEXT("GigLineFacadeGround"), false, this);
|
||||
if (!W->LineTraceSingleByChannel(GroundHit2,
|
||||
FVector(X0, 0.0f, 3000.f), FVector(X0, 0.0f, -1000.f),
|
||||
ECC_Visibility, GQ2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const float ProbeZ2 = GroundHit2.ImpactPoint.Z + 160.f;
|
||||
FHitResult Hit2;
|
||||
FCollisionQueryParams Q2(TEXT("GigLineFacadeProbe"), false, this);
|
||||
const bool bHit2 = W->LineTraceSingleByChannel(Hit2,
|
||||
FVector(X0, 0.0f, ProbeZ2),
|
||||
FVector(X0, Dir * 4000.f, ProbeZ2),
|
||||
ECC_Visibility, Q2);
|
||||
++FacadeSamples;
|
||||
const bool bBlocking = bHit2 && Hit2.GetActor() != nullptr;
|
||||
if (!bBlocking)
|
||||
{
|
||||
++Facades;
|
||||
UE_LOG(LogTemp, Log,
|
||||
TEXT("GigLine QA: facade probe miss at X %.0f dir %.0f (no blocking hit in 40 m)"),
|
||||
X0, Dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
UE_LOG(LogTemp, Log,
|
||||
TEXT("GigLine QA: facade probe %d/%d directions without a blocking wall -> %s"),
|
||||
Facades, FacadeSamples,
|
||||
Facades == 0 ? TEXT("PASS") : TEXT("FAIL"));
|
||||
}
|
||||
|
||||
// 7.0: verdict, then stop ticking.
|
||||
@@ -396,12 +562,15 @@ void AGigLineCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputCo
|
||||
Input->BindAction(JumpAction, ETriggerEvent::Started, this, &AGigLineCharacter::StartJump);
|
||||
Input->BindAction(JumpAction, ETriggerEvent::Completed, this, &AGigLineCharacter::StopJump);
|
||||
}
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine IN: bound %d actions; ctx=%s move=%s look=%s jmp=%s"),
|
||||
(Input ? 1 : 0),
|
||||
MappingContext ? TEXT("ok") : TEXT("nil"),
|
||||
MoveAction ? TEXT("ok") : TEXT("nil"),
|
||||
LookAction ? TEXT("ok") : TEXT("nil"),
|
||||
JumpAction ? TEXT("ok") : TEXT("nil"));
|
||||
if (CVarGigLineSetupLogs.GetValueOnGameThread())
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine IN: bound %d actions; ctx=%s move=%s look=%s jmp=%s"),
|
||||
(Input ? 1 : 0),
|
||||
MappingContext ? TEXT("ok") : TEXT("nil"),
|
||||
MoveAction ? TEXT("ok") : TEXT("nil"),
|
||||
LookAction ? TEXT("ok") : TEXT("nil"),
|
||||
JumpAction ? TEXT("ok") : TEXT("nil"));
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
@@ -428,8 +597,6 @@ namespace
|
||||
|
||||
void AGigLineCharacter::MoveForward(const FInputActionValue& Value)
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine IN: MoveForward fired, axis=%.3f type=%d"),
|
||||
FMath::Abs(SignedAxis(Value, false)), (int)Value.GetValueType());
|
||||
ApplyMove(FMath::Abs(SignedAxis(Value, false)), 0.0f);
|
||||
}
|
||||
|
||||
@@ -453,8 +620,6 @@ void AGigLineCharacter::ApplyMove(float Forward, float Strafe)
|
||||
const AController* Controller = GetController();
|
||||
if (!Controller)
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine IN: ApplyMove called but NO controller (fwd %.2f strafe %.2f)"),
|
||||
Forward, Strafe);
|
||||
return;
|
||||
}
|
||||
if (FMath::IsNearlyZero(Forward) && FMath::IsNearlyZero(Strafe))
|
||||
@@ -468,8 +633,6 @@ void AGigLineCharacter::ApplyMove(float Forward, float Strafe)
|
||||
|
||||
AddMovementInput(ForwardDir, Forward);
|
||||
AddMovementInput(RightDir, Strafe);
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine IN: ApplyMove fwd %.2f strafe %.2f controller=%s yaw=%.0f"),
|
||||
Forward, Strafe, *Controller->GetName(), Controller->GetControlRotation().Yaw);
|
||||
}
|
||||
|
||||
void AGigLineCharacter::Look(const FInputActionValue& Value)
|
||||
|
||||
@@ -26,4 +26,15 @@ void AGigLineHUD::DrawHUD()
|
||||
|
||||
DrawText(AttributionLine, Ink, X, Y, nullptr, TextScale);
|
||||
DrawText(LicenceLine, Ink, X, Y + LineHeight, nullptr, TextScale * 0.82f);
|
||||
|
||||
// Control legend, bottom-right (0.1.0 release check: controls shown in-game).
|
||||
if (!ControlsLine.IsEmpty())
|
||||
{
|
||||
float IgnoredW = 0.0f, ControlsH = 0.0f;
|
||||
GetTextSize(ControlsLine, IgnoredW, ControlsH, nullptr, TextScale * 0.82f);
|
||||
const float CX = Canvas->ClipX - MarginPx - IgnoredW;
|
||||
const float CY = Canvas->ClipY - MarginPx - ControlsH;
|
||||
DrawText(ControlsLine, Shadow, CX + 1.0f, CY + 1.0f, nullptr, TextScale * 0.82f);
|
||||
DrawText(ControlsLine, Ink, CX, CY, nullptr, TextScale * 0.82f);
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,14 @@ public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attribution")
|
||||
FString AttributionLine = TEXT("\u00A9 OpenStreetMap contributors");
|
||||
|
||||
/**
|
||||
* Control legend, shown bottom-right so the 0.1.0 demo documents its own
|
||||
* controls in-game (release check: "controls documented in Docs/ and shown
|
||||
* 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");
|
||||
|
||||
/** 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");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "GigLineShotActor.h"
|
||||
|
||||
#include "Camera/CameraActor.h"
|
||||
#include "Camera/CameraComponent.h"
|
||||
#include "Engine/World.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "UnrealClient.h"
|
||||
@@ -21,6 +22,14 @@ void AGigLineShotActor::Tick(float DeltaSeconds)
|
||||
{
|
||||
Super::Tick(DeltaSeconds);
|
||||
|
||||
// QA mode: the -qaselftest run owns the process (it walks, resets, pauses,
|
||||
// transects, then quits on its own). Skip the screenshot rounds so the game
|
||||
// does not exit mid-test via bQuitWhenDone.
|
||||
if (FParse::Param(FCommandLine::Get(), TEXT("qaselftest")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (bWaitingForSettle)
|
||||
{
|
||||
SettleCountdown -= DeltaSeconds;
|
||||
@@ -75,6 +84,15 @@ 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]);
|
||||
}
|
||||
}
|
||||
PlayerController->SetViewTarget(CurrentCamera);
|
||||
|
||||
SettleCountdown = SettleSeconds;
|
||||
|
||||
@@ -36,6 +36,16 @@ 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;
|
||||
|
||||
@@ -27,6 +27,7 @@ Tools that build 3D content from these sources live next to their output in `Art
|
||||
| `elevation_probe.py` | Samples 3DEP for relief and records the baseline for the release checks |
|
||||
| `dem_stats.py` | Stdlib reader for the tiled GeoTIFF the 3DEP service returns; GDAL does not run on this host |
|
||||
| `asset_metadata.py` | Writes and audits `<asset>.geo.json` sidecars: position, provenance, conversion |
|
||||
| `mapbox_buildings.py` | Cross-checks every building's height against Mapbox `mapbox-streets-v8` building data (Tilequery per centroid), merging our OSM height + source with theirs and flagging matches, close, disagreements, non-building volumes. Needs `MAPBOX_ACCESS_TOKEN` in `.env`; caches responses under `Geo/raw/mapbox-cache/`, and **both the cache and its report are gitignored** (Mapbox terms restrict redistribution) |
|
||||
|
||||
## Unit boundary
|
||||
|
||||
|
||||
@@ -11,20 +11,15 @@
|
||||
| Contents | 3565 nodes, 350 ways; streets: Hollywood Blvd (`secondary`), North Highland Ave (`primary`), North Orange Dr, North McCadden Pl, North Sycamore Ave, Hawthorn Ave, Walk of Fame footway, service alleys |
|
||||
| File | `Geo/raw/osm-api-map-2026-11-06.osm` (1.66 MB, XML) |
|
||||
| SHA-256 | `64615f0db4715c2d50860052e300930c201bbb87e183a1de5985f2d99d83f4e4` |
|
||||
| Licence | ODbL 1.0 — "© OpenStreetMap contributors", <https://www.openstreetmap.org/copyright> |
|
||||
| CRS in source | EPSG:4326 (WGS84 lat/lon) |
|
||||
| Local CRS and origin | not yet chosen. Candidate EPSG:26911 (NAD83 / UTM zone 11N); origin to be recorded when the QGIS step runs |
|
||||
|
||||
## Why this bbox
|
||||
|
||||
It contains the demo block from Highland Avenue to Orange Drive plus the whole north side within 50 m, with enough margin to include the streets' intersection nodes and the alley network behind the south frontage. It deliberately excludes the rest of the boulevard: larger extracts cost processing time and dilute the ODbL review.
|
||||
It contains the demo block from Highland Avenue to Orange Drive plus the whole north side within 50 m, with enough margin to include the streets' intersection nodes and the alley network behind the south frontage. It deliberately excludes the rest of the boulevard: larger extracts cost processing time.
|
||||
|
||||
## Coverage limits
|
||||
|
||||
- Building interiors, heights above the tagged value, and pavement widths are not in this data. Treat all of it as layout.
|
||||
- The `height` tag exists on some buildings and is absent on others (`6838` has only `building:levels`, `6922` has neither). A missing tag is not a zero height.
|
||||
- The extract has no elevation. The DEM decision is still open; see `ROADMAP.md`.
|
||||
|
||||
## Attribution obligation
|
||||
|
||||
Any distributed derived output keeps "© OpenStreetMap contributors" and the licence link. The `0.1.0` release check requires the attribution to be visible in the shipped build.
|
||||
@@ -69,5 +69,4 @@ Unreal's own axes differ (X forward, Y right, Z up). If a level rotates the impo
|
||||
|
||||
## Obligations
|
||||
|
||||
- Any OSM-derived output keeps "© OpenStreetMap contributors" and the licence link. See `THIRD_PARTY_NOTICES.md`.
|
||||
- Elevation data, when chosen, must be reprojected into EPSG:26911 with the origin above, and its vertical datum recorded (NAVD88 is the expected one for US sources; ellipsoidal heights are not interchangeable).
|
||||
Loaded 100 of 159 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user