Compare commits
48
Commits
ee37c7c345
..
v0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0dd4a55c87 | ||
|
|
88b0149bfa | ||
|
|
47bf3a2415 | ||
|
|
3a1db78473 | ||
|
|
b27d30d556 | ||
|
|
4bc41e9faf | ||
|
|
31f073d608 | ||
|
|
71acffec12 | ||
|
|
82a408bc33 | ||
|
|
7e9573a735 | ||
|
|
95011d678a | ||
|
|
abdd7d181f | ||
|
|
16c2bf7c05 | ||
|
|
a3ce9be38a | ||
|
|
2f87002711 | ||
|
|
37a3bfbc3d | ||
|
|
4832e66c00 | ||
|
|
6b4e1252c5 | ||
|
|
fe8740c1e3 | ||
|
|
5aa883eaff | ||
|
|
e93c27054a | ||
|
|
d03829b699 | ||
|
|
72705268b5 | ||
|
|
b577afa730 | ||
|
|
52b1212cf3 | ||
|
|
fee26ade10 | ||
|
|
62a41bf904 | ||
|
|
c626b379d9 | ||
|
|
80ef80b490 | ||
|
|
c3a92d008b | ||
|
|
accf3a5306 | ||
|
|
1dbe737b85 | ||
|
|
fbb3c3fb99 | ||
|
|
c9e933376c | ||
|
|
14265ef230 | ||
|
|
80d75b93c6 | ||
|
|
a181de991c | ||
|
|
91768a9f74 | ||
|
|
ab24ef3b24 | ||
|
|
54b62395b8 | ||
|
|
bc2e421d33 | ||
|
|
985dfcbac7 | ||
|
|
cef87b3d31 | ||
|
|
eb614fe035 | ||
|
|
5ab8b556f3 | ||
|
|
91fd123ab4 | ||
|
|
08ac6a22cc | ||
|
|
84295a1f03 |
No files matched your search
+2
-4
@@ -52,10 +52,8 @@ Thumbs.db
|
||||
# Blender/Blosm working caches
|
||||
/Art/blender/.cache/
|
||||
|
||||
# Rendered screenshots for visual review — generated locally, never committed.
|
||||
# Keeps only the review README (see AGENTS.md Visual review).
|
||||
/Screenshots/*
|
||||
!/Screenshots/README.md
|
||||
# Rendered screenshots for visual review — committed with the repo (see
|
||||
# AGENTS.md Visual review) so reviewers see what the build rendered.
|
||||
|
||||
# Scratch files
|
||||
.tmp-*
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# Prettier ignores — don't format generated or binary content
|
||||
Binaries/
|
||||
Intermediate/
|
||||
Saved/
|
||||
*.uasset
|
||||
*.umap
|
||||
*.blend
|
||||
*.png
|
||||
*.jpg
|
||||
*.jpeg
|
||||
*.gif
|
||||
*.webp
|
||||
|
||||
# Vendored third-party content — never reformat
|
||||
.agents/
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"proseWrap": "never",
|
||||
"tabWidth": 2,
|
||||
"useTabs": false
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
# AGENTS.md
|
||||
|
||||
Instructions and living roadmap for autonomous agents, AI coding assistants, and contributors working on this repository.
|
||||
Working rules for autonomous agents, AI coding assistants, and contributors. Planned work is tracked in **[TODO.md](TODO.md)**; development direction is in **[ROADMAP.md](ROADMAP.md)**.
|
||||
|
||||
## Project Goal
|
||||
|
||||
Develop and test on Linux; ship Windows and Linux builds, with Linux as the primary development and CI platform. The game supports walking and driving through a real city.
|
||||
The initial release is **`0.1.0`**: a walkable exterior demo around **6840 Hollywood Blvd / El Capitan Entertainment Centre**, including buildings on the south side of Hollywood Blvd between Highland Avenue and Orange Drive, plus buildings whose footprint lies within 50 m of the north-side frontage.
|
||||
Develop and test on Linux; ship Windows and Linux builds, with Linux as the primary development and CI platform. The game supports walking and driving through a real city. The initial release is **`0.1.0`**: a walkable exterior demo around **6840 Hollywood Blvd / El Capitan Entertainment Centre**, including buildings on the south side of Hollywood Blvd between Highland Avenue and Orange Drive, plus buildings whose footprint lies within 50 m of the north-side frontage.
|
||||
|
||||
- Verify the address, building footprints, and demo boundaries **before** modeling.
|
||||
- If the address, footprint, or boundary cannot be verified from permitted sources, do not model that structure. Record the unresolved item in `Geo/` with the sources checked and continue with verified structures only.
|
||||
@@ -14,14 +13,14 @@ The initial release is **`0.1.0`**: a walkable exterior demo around **6840 Holly
|
||||
|
||||
## Toolchain
|
||||
|
||||
| Tool | Role |
|
||||
| --- | --- |
|
||||
| Unreal Engine | Rendering, gameplay, World Partition, Chaos Vehicles |
|
||||
| Blender | Modeling, UVs, baking, collision meshes, character prep |
|
||||
| MPFB | Human base meshes; baked materials, deform skeletons |
|
||||
| QGIS | Geographic cleanup, coordinate conversion, elevation prep |
|
||||
| Blosm | OSM building/street blockouts in Blender |
|
||||
| Git + Git LFS | Source control, large assets, tagged releases |
|
||||
| Tool | Role |
|
||||
| ------------- | --------------------------------------------------------- |
|
||||
| Unreal Engine | Rendering, gameplay, World Partition, Chaos Vehicles |
|
||||
| Blender | Modeling, UVs, baking, collision meshes, character prep |
|
||||
| MPFB | Human base meshes; baked materials, deform skeletons |
|
||||
| QGIS | Geographic cleanup, coordinate conversion, elevation prep |
|
||||
| Blosm | OSM building/street blockouts in Blender |
|
||||
| Git + Git LFS | Source control, large assets, tagged releases |
|
||||
|
||||
- Pin compatible versions in `Docs/toolchain.md`.
|
||||
- Validate Blender→Unreal mesh, skeleton, material, and scale transfers **before** producing assets.
|
||||
@@ -52,6 +51,65 @@ Semantic Versioning: `MAJOR.MINOR.PATCH`.
|
||||
- During `0.x`, document breaking changes and asset migrations.
|
||||
- `1.0.0` marks the stable, agreed game scope — not completion of a whole city.
|
||||
|
||||
## Roadmap and TODO
|
||||
|
||||
The two files have separate roles:
|
||||
|
||||
- **[TODO.md](TODO.md)** is the single, central task list for all agents: checkbox items, progress marks, and active-section ownership. Put all planned actionable work and follow-ups there. Do not create parallel task lists elsewhere.
|
||||
- **[ROADMAP.md](ROADMAP.md)** holds the development plan: milestone goals, scope, decisions, and release gates for each version. It is a planning document and contains **no checkbox items**. Read it for future direction; record scope changes there as prose, and record the resulting tasks in `TODO.md`.
|
||||
|
||||
Rules for `TODO.md`:
|
||||
|
||||
- Mark items **open** (`- [ ]`), **active/in progress** (`- [A]`, **deferred** `- [D]`), or **done** (`- [x]`). Keep the marks accurate.
|
||||
- When working on a level-three (`###`) section, append `- active - <sessionId>` to its heading, using your current session ID. Remove the suffix when you stop working on that section. Mark the items you are working on `- [A]` or `- [D]`.
|
||||
- Existing in-progress marks can describe unfinished work, not necessarily a live session; use the active heading and assignment details to identify current ownership.
|
||||
- Each version is a `##` heading; task groups are `###` headings with `- [ ]` steps.
|
||||
- When **all** steps in a version section are done: write a short descriptive entry to `CHANGELOG.md`, remove the version section from `TODO.md`, and leave its goal in `ROADMAP.md`.
|
||||
- Release checks for each version are listed at the end of its `TODO.md` section; `Docs/release-checks.md` defines the checks themselves. If a check fails, do not tag the release; open a patch task in `TODO.md` and report the failing check in `CHANGELOG.md`.
|
||||
- Finish each milestone's release checks **before** expanding scope. Cut map size and simulation complexity before compromising the core walking/driving experience.
|
||||
|
||||
## Git workflow
|
||||
|
||||
Every agent works on a dedicated git branch. **Never commit work directly to `main`** (except the coordinator's integration merges).
|
||||
|
||||
Before starting any task:
|
||||
|
||||
1. **Scan `TODO.md`** and pick a task — a level-three (`###`) section — to work on. Use `ROADMAP.md` for the goal and scope of the version you are working in. Prefer sections that are already active (they may be yours) or open items in sections that are not owned by another agent. Check the active heading and assignment details to identify ownership.
|
||||
2. **Create a branch** for that task from the current `main`: `git checkout -b <topic>-<version>` (e.g. `git checkout -b facade-hero-0.1.4`). One branch per task; do not stack unrelated tasks on one branch.
|
||||
3. **Record the active branch in the heading** of the section you are working on in `TODO.md`: append `- active - <sessionId>` (and, if useful, the branch name) to the level-three heading. This is the only way other agents can see that work is in progress on this section.
|
||||
4. **Commit changes often** on that branch — small, focused commits with clear messages. Do not accumulate large uncommitted changes.
|
||||
5. **Push to remote when a checklist item is done** — each completed `- [x]` item in that section should be committed and pushed. This keeps the remote state visible and makes progress recoverable.
|
||||
6. When the section is complete, report the branch, changes, checks, and unresolved issues to the coordinator for integration (see _Parallel agent workflow_ below). Do not merge into `main` yourself unless authorized.
|
||||
|
||||
**Branch hygiene:**
|
||||
|
||||
- Branch from the current `main` revision, not from a stale local checkout.
|
||||
- Keep `TODO.md` checkbox updates on your branch — they are part of the work.
|
||||
- If a task needs a different camera angle, or affects visual review, follow the _Visual review_ section before pushing.
|
||||
- If a task is abandoned, remove the `- active - <sessionId>` suffix from the heading in `TODO.md` and leave the branch for the coordinator to clean up.
|
||||
|
||||
## Parallel agent workflow
|
||||
|
||||
- Designate one coordinator to serialize assignments, updates to the shared `TODO.md`, and integration into `main`. Agree on task and file or asset ownership before work starts; avoid assigning the same section or binary asset to multiple agents at once.
|
||||
- The coordinator records the session ID, branch name, and owned files or assets alongside the active `TODO.md` section on the shared `main` coordination copy. Publish and synchronize this state through the remote when agents use different machines. Branch-local changes are not visible to other worktrees until integrated; `TODO.md` is not an atomic lock.
|
||||
- For parallel implementation, use a separate branch and Git worktree per agent, based on the agreed `main` revision. Do not switch branches in a directory another agent is using. Create branches, commits, pushes, and merges only when authorized; never overwrite another agent's uncommitted work.
|
||||
- Keep ignored local references, generated files, build outputs, and Unreal editor processes in mind: worktrees do not automatically share ignored files, and shared output paths or concurrent editors can collide. Arrange required inputs and isolated outputs before running tools.
|
||||
- Blender and Unreal binary assets cannot be meaningfully merged like text. Give each asset one owner; use Git LFS locks when supported by the remote, but do not assume locking is available.
|
||||
- When work is ready, report the branch, changes, checks, unresolved issues, and relevant memory findings to the coordinator. Review and integrate branches into `main` one at a time, reconcile `TODO.md` and memory changes, and update ownership and status. Follow the visual review and user-approval requirements before marking visual work done.
|
||||
|
||||
## Memory
|
||||
|
||||
**[MEMORY.md](MEMORY.md)** is shared session memory for agents and collaborators. Write to it whenever you:
|
||||
|
||||
- discover a fact about the environment, a tool, or a data source that cost time to establish;
|
||||
- hit a gotcha, trap, or surprising failure mode, and what actually caused it;
|
||||
- find or fix a bug, and how it was found so it can be found again;
|
||||
- make a decision that does not fit a `TODO.md` item, such as a workaround, an assumption, or a rejected approach and why. Record scope or direction decisions in `ROADMAP.md` as prose.
|
||||
|
||||
Keep entries short and dated, with the evidence: the command, the log line, or the file path. Anything that belongs in a formal document (setup steps, tool versions, licensing) goes in `Docs/` as well — memory is for coordination, not a replacement for the record. Do not let `MEMORY.md` become a second task list: planned work stays in `TODO.md`, and development direction stays in `ROADMAP.md`.
|
||||
|
||||
Memory edits on an agent's branch are not shared live. Communicate important findings to the coordinator during work and reconcile them into the shared `main` copy when integrating.
|
||||
|
||||
## Geographic & Art Pipeline
|
||||
|
||||
1. Save a versioned OSM extract; document date, bounds, and source.
|
||||
@@ -69,7 +127,7 @@ Always use a consistent scale test, named export presets, and repeatable import
|
||||
|
||||
`.agents/skills/blender-skills/` holds 94 Blender skill packs (MIT, blender-skills 1.3.0). They are agent instructions, not add-ons, and nothing in the pipeline depends on them.
|
||||
|
||||
**All agents must consult the relevant skill packs before starting a task in that discipline.** Each patch section in `ROADMAP.md` lists its suggested skills; the general mapping is:
|
||||
**All agents must consult the relevant skill packs before starting a task in that discipline.** Each patch section in `TODO.md` lists its suggested skills; the general mapping is:
|
||||
|
||||
- **Modeling / procedural assets** — `blender-modeler`, `procedural-modeling`, `geometry-nodes`, `hard-surface`.
|
||||
- **Façades and architecture** — `archviz`, `historical-worlds` (1920s Hollywood Blvd direction), `realistic-style`.
|
||||
@@ -89,8 +147,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.
|
||||
|
||||
@@ -99,64 +155,9 @@ Always use a consistent scale test, named export presets, and repeatable import
|
||||
Any change that affects the game's appearance — meshes, materials, lighting, levels, character, camera, post-processing — must be rendered for human review:
|
||||
|
||||
1. Run `Tools/render_screenshots.sh` (rebuilds `Game/GigLine/Content/Test/ReviewMap.umap` from current content, runs the game on it, captures the fixed camera list, copies the PNGs into `Screenshots/`).
|
||||
2. Update `Screenshots/README.md`: one row per screenshot with what it shows and an approval state; describe any new shots or scene changes in the same change.
|
||||
3. The PNGs are **never committed** — `.gitignore` keeps only `Screenshots/README.md`. Review happens on disk.
|
||||
4. Wait for the user's approval before treating a visual change as done; record approval by ticking the table in `Screenshots/README.md`.
|
||||
2. Optional pre-check with a vision model: `Tools/judge_screenshot.py --image <shot.png> --expect "<what it should show>"` (or `--shots` to run every shot in the README) flags likely-missing content before a human looks. It reads `OPENROUTER_API_KEY` from `.env` (ignored). Small helper only — it never replaces the human verdict.
|
||||
3. Update `Screenshots/README.md`: one row per screenshot with what it shows and an approval state; describe any new shots or scene changes in the same change.
|
||||
4. The PNGs **are committed** so reviewers see exactly what the build rendered (`.gitignore` does not exclude them). Keep them in sync with this README: drop retired shots in the same change.
|
||||
5. Wait for the user's approval before treating a visual change as done; record approval by ticking the table in `Screenshots/README.md`.
|
||||
|
||||
New content that needs a different camera angle gets a new entry in the shot list in `Tools/ue_build_review_map.py`.
|
||||
|
||||
## Roadmap
|
||||
|
||||
The living roadmap with per-version task checklists is in **[ROADMAP.md](ROADMAP.md)**.
|
||||
|
||||
- `ROADMAP.md` is the single, central TODO and progress list for all agents. Put planned work and actionable follow-ups there rather than creating parallel task lists.
|
||||
- When working on a level-three (`###`) section, append ` - active - <sessionId>` to its heading, using your current session ID. Remove the suffix when you stop working on that section. Mark checklist items currently being worked on `- [-]` or `- [~]`; update the marks as their status changes.
|
||||
- Planned work is listed as checkbox items in `ROADMAP.md`, marked **open** (`- [ ]`), **in progress/active** (`- [-]` or `- [~]`), or **done** (`- [x]`). Keep the marks accurate.
|
||||
- Existing in-progress marks can describe unfinished work, not necessarily a live session; use the active heading and assignment details to identify current ownership.
|
||||
|
||||
- Each version is a `##` heading; task groups are `###` headings with `- [ ]` steps (`[-]` in progress, `[x]` done).
|
||||
- When **all** steps in a version section are done: write a short descriptive entry to `CHANGELOG.md`, then remove the version section from `ROADMAP.md` to keep it concise.
|
||||
- Finish each milestone's release checks **before** expanding scope. Cut map size and simulation complexity before compromising the core walking/driving experience.
|
||||
- Define the release checks in `Docs/release-checks.md`. If a check fails, do not tag the release; open a patch task in `ROADMAP.md` and report the failing check in `CHANGELOG.md`.
|
||||
|
||||
## Git workflow
|
||||
|
||||
Every agent works on a dedicated git branch. **Never commit work directly to `main`** (except the coordinator's integration merges).
|
||||
|
||||
Before starting any task:
|
||||
|
||||
1. **Scan `ROADMAP.md`** and pick a task — a level-three (`###`) section — to work on. Prefer sections that are already active (they may be yours) or open items in sections that are not owned by another agent. Check the active heading and assignment details to identify ownership.
|
||||
2. **Create a branch** for that task from the current `main`: `git checkout -b <topic>-<version>` (e.g. `git checkout -b facade-hero-0.1.4`). One branch per task; do not stack unrelated tasks on one branch.
|
||||
3. **Record the active branch in the heading** of the section you are working on in `ROADMAP.md`: append ` - active - <sessionId>` (and, if useful, the branch name) to the level-three heading. This is the only way other agents can see that work is in progress on this section.
|
||||
4. **Commit changes often** on that branch — small, focused commits with clear messages. Do not accumulate large uncommitted changes.
|
||||
5. **Push to remote when a checklist item is done** — each completed `- [x]` item in that section should be committed and pushed. This keeps the remote state visible and makes progress recoverable.
|
||||
6. When the section is complete, report the branch, changes, checks, and unresolved issues to the coordinator for integration (see *Parallel agent workflow* below). Do not merge into `main` yourself unless authorized.
|
||||
|
||||
**Branch hygiene:**
|
||||
|
||||
- Branch from the current `main` revision, not from a stale local checkout.
|
||||
- Keep `ROADMAP.md` checkbox updates on your branch — they are part of the work.
|
||||
- If a task needs a different camera angle, or affects visual review, follow the *Visual review* section before pushing.
|
||||
- If a task is abandoned, remove the ` - active - <sessionId>` suffix from the heading in `ROADMAP.md` and leave the branch for the coordinator to clean up.
|
||||
|
||||
## Parallel agent workflow
|
||||
|
||||
- Designate one coordinator to serialize assignments, updates to the shared roadmap, and integration into `main`. Agree on task and file or asset ownership before work starts; avoid assigning the same section or binary asset to multiple agents at once.
|
||||
- The coordinator records the session ID, branch name, and owned files or assets alongside the active roadmap section and items on the shared `main` coordination copy. Publish and synchronize this state through the remote when agents use different machines. Branch-local changes are not visible to other worktrees until integrated; the roadmap is not an atomic lock.
|
||||
- For parallel implementation, use a separate branch and Git worktree per agent, based on the agreed `main` revision. Do not switch branches in a directory another agent is using. Create branches, commits, pushes, and merges only when authorized; never overwrite another agent's uncommitted work.
|
||||
- Keep ignored local references, generated files, build outputs, and Unreal editor processes in mind: worktrees do not automatically share ignored files, and shared output paths or concurrent editors can collide. Arrange required inputs and isolated outputs before running tools.
|
||||
- Blender and Unreal binary assets cannot be meaningfully merged like text. Give each asset one owner; use Git LFS locks when supported by the remote, but do not assume locking is available.
|
||||
- When work is ready, report the branch, changes, checks, unresolved issues, and relevant memory findings to the coordinator. Review and integrate branches into `main` one at a time, reconcile roadmap and memory changes, and update ownership and status. Follow the visual review and user-approval requirements before marking visual work done.
|
||||
|
||||
## Memory
|
||||
|
||||
**[MEMORY.md](MEMORY.md)** is shared session memory for agents and collaborators. Write to it whenever you:
|
||||
|
||||
- discover a fact about the environment, a tool, or a data source that cost time to establish;
|
||||
- hit a gotcha, trap, or surprising failure mode, and what actually caused it;
|
||||
- find or fix a bug, and how it was found so it can be found again;
|
||||
- make a decision that does not fit a `ROADMAP.md` checkbox, such as a workaround, an assumption, or a rejected approach and why.
|
||||
|
||||
Keep entries short and dated, with the evidence: the command, the log line, or the file path. Anything that belongs in a formal document (setup steps, tool versions, licensing) goes in `Docs/` as well — memory is for coordination, not a replacement for the record. Do not let `MEMORY.md` become a second roadmap: planned work stays in `ROADMAP.md`.
|
||||
|
||||
Memory edits on an agent's branch are not shared live. Communicate important findings to the coordinator during work and reconcile them into the shared `main` copy when integrating.
|
||||
+12
-12
@@ -56,13 +56,13 @@ LD_LIBRARY_PATH="$B/blender/lib" "$B/blender/blender" -b --factory-startup --p
|
||||
|
||||
Spot checks against the measured cross-sections:
|
||||
|
||||
| Point | Terrain Z |
|
||||
| --- | ---: |
|
||||
| Origin, Hollywood × Highland | 0.00 |
|
||||
| Orange Drive end of the block | +3.39 |
|
||||
| North sidewalk mid-block | +1.43 |
|
||||
| South gutter, mid-block | +0.93 |
|
||||
| Road centre, mid-block | +1.38 |
|
||||
| Point | Terrain Z |
|
||||
| ----------------------------- | --------: |
|
||||
| Origin, Hollywood × Highland | 0.00 |
|
||||
| Orange Drive end of the block | +3.39 |
|
||||
| North sidewalk mid-block | +1.43 |
|
||||
| South gutter, mid-block | +0.93 |
|
||||
| Road centre, mid-block | +1.38 |
|
||||
|
||||
That reproduces both the 3.4 m eastward fall and the ~2 % crown, so the DEM, the cross-sections, and this mesh agree.
|
||||
|
||||
@@ -103,11 +103,11 @@ 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.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 |
|
||||
|
||||
No holes anywhere, so nothing drops the player on either side. The two flagged steps:
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import sys
|
||||
import bpy
|
||||
|
||||
MESHES = ("Terrain_DEM", "Road_Hollywood", "Sidewalk_South", "Sidewalk_North",
|
||||
"Kerb_South", "Kerb_North")
|
||||
"Kerb_South", "Kerb_North", "Crossing_Markings")
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
Binary file not shown.
@@ -63,6 +63,7 @@ APPEND = {
|
||||
"Art/blender/terrain/terrain-2026-11-06.blend": ["Terrain_DEM"],
|
||||
"Art/blender/street/street-2026-11-06.blend": [
|
||||
"Road_Hollywood", "Sidewalk_South", "Sidewalk_North", "Kerb_South", "Kerb_North",
|
||||
"Crossing_Markings",
|
||||
],
|
||||
}
|
||||
BLOCKOUT = "Art/blender/blockout/blockout-2026-11-06.blend"
|
||||
@@ -193,7 +194,14 @@ def main():
|
||||
|
||||
out = os.path.abspath(args.out)
|
||||
os.makedirs(os.path.dirname(out), exist_ok=True)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=out)
|
||||
# *.blend is LFS-lockable, so a fresh checkout marks it read-only. Make it
|
||||
# writable again or the save fails and downstream steps build from a stale
|
||||
# file — which is worse than failing here.
|
||||
if os.path.exists(out) and not os.access(out, os.W_OK):
|
||||
os.chmod(out, 0o644)
|
||||
result = bpy.ops.wm.save_as_mainfile(filepath=out)
|
||||
if result != {"FINISHED"}:
|
||||
raise RuntimeError(f"could not save {out}: {result}")
|
||||
print(f"saved {out}")
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
@@ -18,6 +18,10 @@ Geometry follows the measured profile rather than a guess:
|
||||
kerb (see `Geo/verification/2026-11-06-cross-sections.md`);
|
||||
* kerbs are flushed to 0.02 m within ±2.5 m of each signalised crossing, with a
|
||||
1.5 m blend, so the crossings are actually usable on foot;
|
||||
* each signalised crossing gets continental zebra markings — 0.5 m white bars
|
||||
with 0.5 m gaps across a 3 m band, laid 1 cm above the road surface — so the
|
||||
mid-block crossing (OSM way 385648319, s ≈ 130 m) reads as a designated
|
||||
crossing in game;
|
||||
* sidewalk widths 4.0 m south and 5.3 m north, from the DEM-derived setback
|
||||
arithmetic;
|
||||
* the DEM supplies every elevation, so the 3.4 m fall and the ~2 % crown come
|
||||
@@ -144,7 +148,10 @@ def strip(rows, offsets, z_at, name, material):
|
||||
for r in range(len(rows) - 1):
|
||||
for c in range(cols - 1):
|
||||
i = r * cols + c
|
||||
faces.append((i, i + 1, i + cols + 1, i + cols))
|
||||
# Winding faces up: the natural (i, i+1, i+cols+1, i+cols) order
|
||||
# produces downward normals (every surface faced the ground and was
|
||||
# invisible in engine until the terrain corridor cut exposed it).
|
||||
faces.append((i, i + cols, i + cols + 1, i + 1))
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
mesh.update()
|
||||
@@ -235,7 +242,7 @@ def main():
|
||||
verts.extend([(x, y, z_road), (x, y, z_walk)])
|
||||
for idx in range(len(rows) - 1):
|
||||
i = idx * 2
|
||||
faces.append((i, i + 2, i + 3, i + 1))
|
||||
faces.append((i, i + 1, i + 3, i + 2))
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
mesh.update()
|
||||
@@ -247,6 +254,58 @@ def main():
|
||||
kerb_faces(SOUTH_GUTTER, "Kerb_South")
|
||||
kerb_faces(NORTH_GUTTER, "Kerb_North")
|
||||
|
||||
# Continental zebra markings: one band per signalised crossing. Bars run
|
||||
# along the road (traffic direction), side by side across the carriageway;
|
||||
# a pedestrian crosses perpendicular to them. Lifted 1 cm above the road
|
||||
# surface (which is itself 2 cm above the DEM) to avoid z-fighting.
|
||||
paint = bpy.data.materials.new("Road_Paint_White")
|
||||
paint.diffuse_color = (0.85, 0.85, 0.85, 1.0)
|
||||
|
||||
# All three bands live in ONE object. Creating one object per crossing used
|
||||
# to produce Crossing_Markings, .001 and .002; site assembly appends by the
|
||||
# exact name, so only the first crossing ever reached the game.
|
||||
band_verts, band_faces = [], []
|
||||
|
||||
def add_crossing_band(s_c):
|
||||
# Stripes are emitted as two triangles each: the road crown makes the
|
||||
# quad non-planar and the FBX SDK refuses to triangulate it on import
|
||||
# ("unable to triangulate mesh" — the mesh is omitted entirely).
|
||||
zone = 3.0 # band length along the road, m
|
||||
w, g = 0.5, 0.5 # stripe width / gap, m
|
||||
span = (SOUTH_GUTTER + 0.2, NORTH_GUTTER - 0.2)
|
||||
n = int(zone // (w + g))
|
||||
covered = n * w + (n - 1) * g # actual stripe coverage, centred on s_c
|
||||
for k in range(n):
|
||||
s0 = s_c - covered / 2 + k * (w + g)
|
||||
base_idx = len(band_verts) # sampled positions, then vertices
|
||||
pts = []
|
||||
for s_edge in (s0, s0 + w):
|
||||
base, normal, _ = at(s_edge)
|
||||
for off in span:
|
||||
x = base[0] + off * normal[0]
|
||||
y = base[1] + off * normal[1]
|
||||
# +6 cm above the DEM = 4 cm above the road surface: the road
|
||||
# interpolates the DEM along 1 m rows while the markings sample
|
||||
# only their own corners, so a 1 cm lift left parts of the
|
||||
# stripes sunk under the road and invisible in game.
|
||||
pts.append((x, y, (dem_z(x, y) or 0.0) + 0.06))
|
||||
# pts order: (s0,off_s), (s0,off_n), (s1,off_s), (s1,off_n)
|
||||
band_verts.extend(pts)
|
||||
band_faces.append((base_idx + 0, base_idx + 3, base_idx + 1))
|
||||
band_faces.append((base_idx + 0, base_idx + 2, base_idx + 3))
|
||||
|
||||
for s_c in CROSSINGS:
|
||||
add_crossing_band(s_c)
|
||||
|
||||
mesh = bpy.data.meshes.new("Crossing_Markings")
|
||||
mesh.from_pydata(band_verts, [], band_faces)
|
||||
mesh.update()
|
||||
mesh.materials.append(paint)
|
||||
markings = bpy.data.objects.new("Crossing_Markings", mesh)
|
||||
bpy.context.scene.collection.objects.link(markings)
|
||||
print(f"crossing markings: {len(CROSSINGS)} bands, "
|
||||
f"{len(markings.data.vertices)} verts, {len(markings.data.polygons)} faces")
|
||||
|
||||
for obj in (road, sw_s, sw_n):
|
||||
print(f"{obj.name:16} verts {len(obj.data.vertices):5} faces {len(obj.data.polygons):5}")
|
||||
|
||||
@@ -261,7 +320,14 @@ def main():
|
||||
|
||||
out = os.path.abspath(args.out)
|
||||
os.makedirs(os.path.dirname(out), exist_ok=True)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=out)
|
||||
# *.blend is LFS-lockable, so a fresh checkout marks it read-only. Make it
|
||||
# writable again or the save fails and downstream steps build from a stale
|
||||
# file — which is worse than failing here.
|
||||
if os.path.exists(out) and not os.access(out, os.W_OK):
|
||||
os.chmod(out, 0o644)
|
||||
result = bpy.ops.wm.save_as_mainfile(filepath=out)
|
||||
if result != {"FINISHED"}:
|
||||
raise RuntimeError(f"could not save {out}: {result}")
|
||||
print(f"saved {out}")
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
@@ -53,6 +53,9 @@ def parse_args():
|
||||
ap.add_argument("--ymax", type=float, default=110.0)
|
||||
ap.add_argument("--osm", default=None,
|
||||
help="OSM extract; building outlines are cut out so rooftops never become terrain")
|
||||
ap.add_argument("--corridor", type=float, default=10.7,
|
||||
help="half-width in m of the Hollywood Blvd corridor to cut out so the "
|
||||
"street meshes (road, kerbs, sidewalks) are not buried under the terrain")
|
||||
return ap.parse_args(argv)
|
||||
|
||||
|
||||
@@ -110,6 +113,30 @@ def pts_flat(ring):
|
||||
return ring
|
||||
|
||||
|
||||
def centreline_polyline(osm_path):
|
||||
"""Hollywood Blvd centreline as a local-frame polyline (Highland -> Orange)."""
|
||||
nodes, ways = oc.load(osm_path)
|
||||
line, _, _, _, _ = oc.block_extent(
|
||||
nodes, ways, "Hollywood Boulevard", "North Orange Drive", "North Highland Avenue")
|
||||
utm = project_to_utm(line, find_proj())
|
||||
return [(e - ORIGIN_E, n - ORIGIN_N) for e, n in utm]
|
||||
|
||||
|
||||
def dist_to_polyline(x, y, pts):
|
||||
best = float("inf")
|
||||
for (ax, ay), (bx, by) in zip(pts, pts[1:]):
|
||||
dx, dy = bx - ax, by - ay
|
||||
l2 = dx * dx + dy * dy
|
||||
if l2 == 0.0:
|
||||
continue
|
||||
t = max(0.0, min(1.0, ((x - ax) * dx + (y - ay) * dy) / l2))
|
||||
px, py = ax + t * dx, ay + t * dy
|
||||
d = (x - px) ** 2 + (y - py) ** 2
|
||||
if d < best:
|
||||
best = d
|
||||
return best ** 0.5
|
||||
|
||||
|
||||
def inside(x, y, ring):
|
||||
"""Ray casting point-in-polygon."""
|
||||
hit = False
|
||||
@@ -146,9 +173,12 @@ def main():
|
||||
f"X {args.xmin}..{args.xmax}, Y {args.ymin}..{args.ymax}")
|
||||
|
||||
polys = []
|
||||
corridor = None
|
||||
if args.osm:
|
||||
polys = building_polygons(args.osm)
|
||||
print(f"masking {len(polys)} building footprints")
|
||||
corridor = centreline_polyline(args.osm)
|
||||
print(f"cutting {args.corridor} m corridor around the Hollywood Blvd centreline")
|
||||
|
||||
verts = []
|
||||
ok = []
|
||||
@@ -168,6 +198,10 @@ def main():
|
||||
verts.append((x, y, 0.0))
|
||||
ok.append(False)
|
||||
continue
|
||||
if corridor is not None and dist_to_polyline(x, y, corridor) <= args.corridor:
|
||||
verts.append((x, y, 0.0))
|
||||
ok.append(False)
|
||||
continue
|
||||
z = values[ri * width + ci]
|
||||
if nodata is not None and z == nodata:
|
||||
verts.append((x, y, 0.0))
|
||||
@@ -190,6 +224,12 @@ def main():
|
||||
mesh = bpy.data.meshes.new("Terrain_DEM")
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
mesh.update()
|
||||
# Without a material the mesh imports into Unreal with the default white
|
||||
# material and blows out under the demo sun. A muted earth tone keeps the
|
||||
# ground reading as ground.
|
||||
ground = bpy.data.materials.new("Ground_Earth")
|
||||
ground.diffuse_color = (0.30, 0.28, 0.25, 1.0)
|
||||
mesh.materials.append(ground)
|
||||
obj = bpy.data.objects.new("Terrain_DEM", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
|
||||
@@ -212,7 +252,14 @@ def main():
|
||||
|
||||
out = os.path.abspath(args.out)
|
||||
os.makedirs(os.path.dirname(out), exist_ok=True)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=out)
|
||||
# *.blend is LFS-lockable, so a fresh checkout marks it read-only. Make it
|
||||
# writable again or the save fails and downstream steps build from a stale
|
||||
# file — which is worse than failing here.
|
||||
if os.path.exists(out) and not os.access(out, os.W_OK):
|
||||
os.chmod(out, 0o644)
|
||||
result = bpy.ops.wm.save_as_mainfile(filepath=out)
|
||||
if result != {"FINISHED"}:
|
||||
raise RuntimeError(f"could not save {out}: {result}")
|
||||
print(f"saved {out}")
|
||||
|
||||
|
||||
|
||||
+76
-3
@@ -2,8 +2,81 @@
|
||||
|
||||
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.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.0...main
|
||||
[0.1.0]: https://git.allucanget.biz/zwitschi/gig-line/releases/tag/v0.1.0
|
||||
@@ -0,0 +1,35 @@
|
||||
# Introduction and Goals
|
||||
|
||||
Gig Line is a Linux-first Unreal Engine 5 game that reconstructs a walkable (and eventually drivable) stretch of Hollywood Boulevard around 6840 Hollywood Blvd / El Capitan Entertainment Centre. The initial release (`0.1.0`) delivers a first-person walking demo of the south-side block between Highland Avenue and Orange Drive, plus north-side buildings within 50 m of the frontage.
|
||||
|
||||
## Requirements Overview
|
||||
|
||||
| ID | Description |
|
||||
| --- | --- |
|
||||
| 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-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 |
|
||||
| F-8 | 60 FPS at 1440p on the documented workstation and quality preset |
|
||||
|
||||
Full requirements and progress are in [TODO.md](../../TODO.md); version direction is in [ROADMAP.md](../../ROADMAP.md).
|
||||
|
||||
## Quality Goals
|
||||
|
||||
| Priority | Quality Goal | Scenario |
|
||||
| --- | --- | --- |
|
||||
| 1 | **Performance efficiency** | 60 FPS at 2560×1440 on RTX 5070 Ti, documented preset, no frame generation |
|
||||
| 2 | **Terrain walkability** | Zero raycast holes across both sidewalks and the carriageway (84-sample transect passes) |
|
||||
| 3 | **Geographic fidelity** | Building placement, street scale, and façade identity match reference photography |
|
||||
| 4 | **Build reproducibility** | Fresh checkout + documented engine path produces a runnable Linux build |
|
||||
| 5 | **Provenance** | Every committed asset has recorded provenance |
|
||||
|
||||
## Stakeholders
|
||||
|
||||
| Role | Contact | Expectations |
|
||||
| --- | --- | --- |
|
||||
| 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 |
|
||||
@@ -0,0 +1,32 @@
|
||||
# Architecture Constraints
|
||||
|
||||
## Technical Constraints
|
||||
|
||||
| Constraint | Explanation |
|
||||
| --- | --- |
|
||||
| Engine: Unreal 5.8.4 | Pinned; no bundled Linux clang — uses system clang 23.1.4. Game targets use `BuildSettingsVersion.V7`. |
|
||||
| Platform: Linux first | Developed and tested on Bazzite Linux (Fedora 45 base). Windows builds ship but are secondary. |
|
||||
| GPU: RTX 5070 Ti 16 GB | Dev workstation target. Vulkan RHI; open kernel module driver 615.78.08. |
|
||||
| No static lighting | `r.AllowStaticLighting=False`. The demo is a fixed daytime scene lit entirely dynamically to avoid "LIGHTING NEEDS TO BE REBUILT" warnings. |
|
||||
| Blender→UE via FBX | Flat diffuse colours do not survive FBX→Unreal import. All materials are authored in UE. |
|
||||
| Terrain collision | UE 5.8 exposes `CollisionTraceFlag` (not `bUseComplexAsSimpleCollision`); Python cannot set it. Must use C++ helper `UGigLineAssetTools::SetComplexCollisionAsSimple`. |
|
||||
| Coordinate convention | South = +Y, north = −Y (ground truth = street mesh orientation). 1 m in Blender = 100 cm in Unreal. |
|
||||
|
||||
## Organisational Constraints
|
||||
|
||||
| Constraint | Explanation |
|
||||
| --- | --- |
|
||||
| Squash-merge to `main` | Every PR is squash-merged for a linear history. No direct commits to `main` except integration merges. |
|
||||
| Visual review required | Any change affecting appearance must produce screenshots for human approval before being marked done. |
|
||||
| Asset provenance | Every committed asset must have verified licence and redistribution permission recorded in `THIRD_PARTY_NOTICES.md`. |
|
||||
| Reference photography | Local only, gitignored, never committed. Used for façade work under fair-use / internal-development rationale. |
|
||||
| LFS for binaries | `.uasset`, `.umap`, `.blend` tracked with Git LFS; `Binaries/`, `Intermediate/`, `Saved/` are gitignored. |
|
||||
|
||||
## Conventions
|
||||
|
||||
| Convention | Explanation |
|
||||
| --- | --- |
|
||||
| Semantic versioning | `MAJOR.MINOR.PATCH`. `0.x` documents breaking changes. `1.0.0` = agreed game scope. |
|
||||
| Task tracking in `TODO.md` | Single central task list. `- [ ]` open, `- [-]` or `- [~]` in progress, `- [x]` done, `- [A]` active, `- [D]` deferred. |
|
||||
| Headless Blender | All Blender work driven by `bpy` scripts in `Tools/`, not MCP. Blender skills are method guides only. |
|
||||
| Named export presets | `ue_metric_v1` for static meshes, `ue_character_v1` for skeletal. Every exporter calls the metre→centimetre conversion. |
|
||||
@@ -0,0 +1,22 @@
|
||||
# Context and Scope
|
||||
|
||||
## Business Context
|
||||
|
||||
The system is a self-contained single-player game. It has no server backend, no multiplayer, and no live service dependencies. Its communication partners are:
|
||||
|
||||
| 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 | — |
|
||||
| Filesystem | Engine content (`.uasset`, `.umap`), Blender sources (`.blend`), DEM GeoTIFF | Saved maps, screenshots, packaged build |
|
||||
|
||||
## Technical Context
|
||||
|
||||
| Channel | Protocol / Format | Purpose |
|
||||
| --- | --- | --- |
|
||||
| Keyboard + mouse | Enhanced Input (IA\_\*, IMC_Default) | First-person movement, look, jump, reset, pause |
|
||||
| Vulkan RHI | GPU rendering | Scene display at 60 FPS / 1440p |
|
||||
| FBX | Autodesk FBX (binary) | Blender→Unreal mesh and skeleton transfer |
|
||||
| GeoTIFF | 3DEP 1 m DEM | Terrain heightfield source |
|
||||
| OSM XML | Overpass API extract | Building footprint and street layout source |
|
||||
| Git / LFS | HTTPS to Gitea remote | Source control, binary asset storage |
|
||||
@@ -0,0 +1,39 @@
|
||||
# Solution Strategy
|
||||
|
||||
## Technology decisions
|
||||
|
||||
- **Unreal Engine 5.8.4** for rendering, World Partition, physics (Chaos), and the Enhanced Input system. Chosen for its Linux support, open C++ source, and real-time rendering quality.
|
||||
|
||||
- **Blender 5.2 LTS** for all 3D modelling, UV work, and collision mesh authoring. Driven headlessly via `bpy` Python scripts, not MCP.
|
||||
|
||||
- **QGIS 4.2.3** for geographic data cleanup, coordinate conversion (EPSG:26911), and DEM preparation.
|
||||
|
||||
- **Blosm 2.7.28** for OSM building and street blockouts inside Blender (layout data only — not authoritative façade, height, or pavement detail).
|
||||
|
||||
- **MPFB 2.0.18** for human base meshes and baked materials (NPCs in `0.2.0+`).
|
||||
|
||||
## Top-level decomposition
|
||||
|
||||
The system is a single Unreal game module (`GigLine`) with no plugins beyond `EnhancedInput`. Content is organised by domain under `/Game/Hollywood/`:
|
||||
|
||||
- `Site/` — terrain, road, sidewalks, kerbs, crossing markings, site materials
|
||||
|
||||
- `ElCapitan/` — hero façade mesh and materials
|
||||
|
||||
- `Frontage/` — south-side block building meshes
|
||||
|
||||
- `North/` — north-side building meshes
|
||||
|
||||
- `Input/` — Enhanced Input actions and mapping context
|
||||
|
||||
All modelling and geographic processing live outside the game project in `Art/`, `Geo/`, and `Tools/`. The `Game/` directory contains only engine-ready content and C++ source.
|
||||
|
||||
## Quality achievement
|
||||
|
||||
- **Performance**: dynamic-only lighting (no build step, no static shadow maps), world-space-noise materials (cheap procedural variation), minimal overdraw (simple street-level geometry).
|
||||
|
||||
- **Walkability**: complex-as-simple collision on terrain (C++ helper forces the flag on the asset), block-all collision on every site mesh, swept transect QA verifies zero raycast holes.
|
||||
|
||||
- **Fidelity**: geographic data verified before modelling (footprints, bounds, DEM); building placement checked against street mesh, never in isolation; reference photography drives façade work.
|
||||
|
||||
- **Reproducibility**: pinned toolchain in `Docs/toolchain.md`, named export presets, `Tools/check-lfs.sh` gate, fresh-checkout build procedure in `Docs/build.md`.
|
||||
@@ -0,0 +1,96 @@
|
||||
# Building Block View
|
||||
|
||||
## Whitebox Overall System
|
||||
|
||||
The system decomposes into five top-level building blocks:
|
||||
|
||||
| **Name** | **Responsibility** |
|
||||
| --- | --- |
|
||||
| Gameplay | Player pawn, movement, camera, input bindings, HUD, game mode |
|
||||
| Site | Terrain, road, sidewalks, kerbs, crossing markings, site-level collision |
|
||||
| Façades | Hero façade (El Capitan), south block frontage, north-side buildings |
|
||||
| Art Pipeline | Blender models, geographic data, export scripts, import scripts |
|
||||
| QA & Review | Screenshot pipeline, self-test harness, visual review workflow |
|
||||
|
||||
### Gameplay
|
||||
|
||||
C++ classes in `Game/GigLine/Source/GigLine/`:
|
||||
|
||||
| Class | Responsibility | Key detail |
|
||||
| --- | --- | --- |
|
||||
| `AGigLineCharacter` | First-person player pawn | 1.75 m capsule, 4.5 m/s walk, Enhanced Input bindings, reset, pause, `-qaselftest` harness |
|
||||
| `AGigLineGameModeBase` | Sets game mode and HUD class | `HUDClass = AGigLineHUD` |
|
||||
| `AGigLineHUD` | HUD text overlay | `DrawText` every frame, bottom-left |
|
||||
| `AGigLineNPCPerson` | NPC prefab (future) | Capsule + visible MPFB body, feet on Z 0 |
|
||||
| `AGigLineShotActor` | Screenshot camera walker | Drives cameras in `-game` mode, `FScreenshotRequest`, quits |
|
||||
| `UGigLineAssetTools` | C++ collision helpers | `SetComplexCollisionAsSimple` on `StaticMesh` assets |
|
||||
|
||||
Input assets in `/Game/Input/`: `IA_Move`, `IA_MoveBack`, `IA_Strafe`, `IA_StrafeLeft`, `IA_Look`, `IA_Jump`, `IA_Reset`, `IA_Pause`, bound in `IMC_Default`.
|
||||
|
||||
### Site
|
||||
|
||||
Static meshes and materials in `/Game/Hollywood/Site/`:
|
||||
|
||||
| Asset | Role |
|
||||
| --- | --- |
|
||||
| `Terrain_DEM` | Ground heightfield (complex-as-simple collision) |
|
||||
| `Road_Hollywood` | Carriageway surface (block-all collision) |
|
||||
| `Sidewalk_North`, `Sidewalk_South` | Pedestrian surfaces (block-all collision) |
|
||||
| `Kerb_North`, `Kerb_South` | Kerb edges |
|
||||
| `Crossing_Markings` | Zebra markings at signalised crossing |
|
||||
| `M_Asphalt`, `M_Concrete`, `M_KerbConcrete`, `M_Ground`, `M_RoadPaint`, `M_Stucco`, `M_FacadeStone` | Procedural materials (world-space-noise blend + roughness) |
|
||||
|
||||
### Façades
|
||||
|
||||
| Asset | Role |
|
||||
| --- | --- |
|
||||
| `SM_HeroFacade` | El Capitan Entertainment Centre detailed façade |
|
||||
| `SM_ElCapitanTheatre`, `SM_Budget_6822`, `SM_Hollywood_6806`, `SM_Hollywood_6902`, `SM_Hollywood_6922`, `SM_LeeDrug_6800` | South block frontage (6 buildings) |
|
||||
| `SM_Madame_Tussauds`, `SM_TCL_Chinese_Theatre` | North-side buildings (within 50 m) |
|
||||
|
||||
### Art Pipeline
|
||||
|
||||
Python scripts in `Tools/` drive the content pipeline:
|
||||
|
||||
| Script group | Purpose |
|
||||
| --- | --- |
|
||||
| `export_*`, `hero_facade_build` | Blender headless export (FBX) |
|
||||
| `ue_import_*`, `ue_hero_facade_import` | Unreal headless import + placement |
|
||||
| `ue_build_demo_map`, `ue_build_review_map` | Map assembly (lighting, meshes, player start) |
|
||||
| `ue_create_input_assets` | Enhanced Input actions + mapping context |
|
||||
| `render_screenshots.sh` | Screenshot capture pipeline |
|
||||
| `judge_screenshot.py` | Vision-model pre-check (optional) |
|
||||
|
||||
Source data: `Art/blender/` (`.blend` files), `Geo/` (OSM extracts, DEM, verification docs).
|
||||
|
||||
### QA & Review
|
||||
|
||||
- `AGigLineShotActor` walks fixed cameras in `-game` mode, captures PNGs, and quits.
|
||||
|
||||
- `-qaselftest` switch on `AGigLineCharacter` runs a core-ticker harness: walk 2 s, reset, pause/unpause, ground transect — all PASS/FAIL logged.
|
||||
|
||||
- `Screenshots/` holds committed PNGs and `README.md` approval table.
|
||||
|
||||
## Level 2
|
||||
|
||||
### White Box Gameplay — Input Flow
|
||||
|
||||
1. `AGigLineCharacter::SetupPlayerInputComponent` binds `IA_*` actions to `MoveForward`, `MoveBack`, `MoveStrafe`, `MoveLeft`, `Look`, `StartJump`/`StopJump`, `ResetToStart`, `TogglePause`.
|
||||
|
||||
2. Movement delegates to `ApplyMove(Forward, Strafe)` → `AddMovementInput`.
|
||||
|
||||
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.
|
||||
|
||||
5. `TogglePause` toggles `APlayerController::SetPause`, swaps input mode between `FInputModeGameOnly` and `FInputModeUIOnly`, shows/hides mouse cursor.
|
||||
|
||||
### White Box Site — Collision Model
|
||||
|
||||
- Every site mesh (road, sidewalks, kerbs, crossing) is `STATIC` with `BlockAll` collision.
|
||||
|
||||
- `Terrain_DEM` uses `CTF_UseComplexAsSimple` (forced by `UGigLineAssetTools` at build time, saved on the asset).
|
||||
|
||||
- The terrain corridor is carved to half-width 10.7 m around the road centreline so the player can walk the carriageway and both sidewalks.
|
||||
|
||||
- Ground Z: north sidewalk 395, road 366, south sidewalk 317 (3.94 m fall along the block, matching the DEM).
|
||||
@@ -0,0 +1,53 @@
|
||||
# Runtime View
|
||||
|
||||
## Walk the block
|
||||
|
||||
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.
|
||||
|
||||
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).
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## QA self-test (`-qaselftest`)
|
||||
|
||||
1. Core ticker starts at `BeginPlay` (not a world timer — world timers freeze on pause).
|
||||
|
||||
2. 1.0 s: begin driving forward input.
|
||||
|
||||
3. 3.0 s: stop, measure displacement (> 200 cm = PASS).
|
||||
|
||||
4. 3.5 s: call `ResetToStart`.
|
||||
|
||||
5. 4.0 s: measure distance from spawn (< 100 cm = PASS).
|
||||
|
||||
6. 4.5 s: call `TogglePause`, check `IsGamePaused()` (= true = 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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Deployment View
|
||||
|
||||
## Infrastructure Level 1 — Development Workstation
|
||||
|
||||
| Node | Specification |
|
||||
| --- | --- |
|
||||
| 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) |
|
||||
|
||||
Motivation
|
||||
Single-machine development. No build farm, no CI pipeline yet. The engine, toolchain, and GPU are all on the same host.
|
||||
|
||||
Quality and Performance Features
|
||||
|
||||
- Vulkan RHI on RTX 5070 Ti, open kernel module driver 615.78.08.
|
||||
|
||||
- System clang 23.1.4 (no bundled engine clang) compiles game code.
|
||||
|
||||
- Build time: ~90 s incremental, ~5 min clean for the editor target.
|
||||
|
||||
Mapping of Building Blocks to Infrastructure
|
||||
|
||||
| Building Block | Infrastructure Element |
|
||||
| --- | --- |
|
||||
| Gameplay (C++) | Dev workstation — compiled to `libUnrealEditor-GigLine.so`, loaded by engine |
|
||||
| Site + Façades (UE content) | Dev workstation — `.uasset`/`.umap` in `Game/GigLine/Content/` |
|
||||
| Art Pipeline (Python/Blender) | Dev workstation — headless Blender 5.2 Flatpak, Python 3.14.8 |
|
||||
| QA & Review | Dev workstation — game run in `-game` mode, PNGs to `Screenshots/` |
|
||||
| Git remote | `git.allucanget.biz` — Gitea instance, LFS-enabled |
|
||||
|
||||
## Infrastructure Level 2 — Packaged Build
|
||||
|
||||
| Element | Detail |
|
||||
| --- | --- |
|
||||
| Linux packaged build | Development config, ~1.1 GiB. Contains engine binaries, game module, cooked content, no editor. |
|
||||
| Run command | `./GigLine.sh -windowed -ResX=2560 -ResY=1440` |
|
||||
|
||||
No server infrastructure. The game is a standalone executable.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Cross-cutting Concepts
|
||||
|
||||
## Coordinate system and scale
|
||||
|
||||
- **Source CRS**: EPSG:26911 (UTM zone 11N). Local origin recorded in `Geo/`.
|
||||
|
||||
- **Blender**: metres. East → +X, north → +Y (Blender convention).
|
||||
|
||||
- **Unreal**: centimetres. East → +X, north → **−Y** (confirmed from street mesh: `Sidewalk_North` Y −1912..−1053, `Sidewalk_South` Y +718..+1447).
|
||||
|
||||
- **Conversion**: every exporter calls the metre→centimetre function. Import presets (`ue_metric_v1`, `ue_character_v1`) are named and repeatable.
|
||||
|
||||
Rule: place a building only against a neighbour or street mesh, never in isolation.
|
||||
|
||||
## Collision model
|
||||
|
||||
- Site meshes (road, sidewalks, kerbs, crossing): `STATIC` mobility, `BlockAll` collision profile.
|
||||
|
||||
- Terrain: `STATIC`, `CTF_UseComplexAsSimple` (forced by `UGigLineAssetTools` at build time, saved on the asset — the flag lives on the `UStaticMesh`/`UBodySetup`, not the map).
|
||||
|
||||
- Buildings: `STATIC`, simple collision (convex hull from mesh).
|
||||
|
||||
- Rule: the collision flag must be saved on the asset. Saving only the `.umap` silently reverts it.
|
||||
|
||||
## Lighting
|
||||
|
||||
- Dynamic-only: `r.AllowStaticLighting=False`. All lights are `MOVABLE`.
|
||||
|
||||
- Fixed daytime rig: directional light (sun, −62° pitch), sky light, exponential height fog, post-process volume with fixed exposure.
|
||||
|
||||
- Rationale: avoids "LIGHTING NEEDS TO BE REBUILT" warnings; no bake step; the scene never changes time of day.
|
||||
|
||||
## Materials
|
||||
|
||||
- All materials authored in Unreal, not carried from Blender (FBX import loses diffuse colours).
|
||||
|
||||
- World-space-noise blend nodes for cheap procedural variation (asphalt grain, concrete speckle, stucco roughness).
|
||||
|
||||
- Instance parameters where tunable (e.g. wetness, ageing).
|
||||
|
||||
## Geographic data provenance
|
||||
|
||||
- OSM extract is layout data only — not authoritative for façade, height, or pavement detail.
|
||||
|
||||
- DEM from 3DEP 1 m; elevation verified against cross-sections in `Geo/verification/`.
|
||||
|
||||
- Reference photography (local, gitignored) drives manual façade work.
|
||||
|
||||
## QA harness
|
||||
|
||||
- `-qaselftest`: core ticker (not world timer) so pause cannot freeze the test that proves pause works.
|
||||
|
||||
- `GigLine SIM` / `GigLine GROUND`: TEMP diagnostic logging, to be removed before release tag.
|
||||
|
||||
- Screenshot approval: human must tick `Screenshots/README.md` before visual work is marked done.
|
||||
@@ -0,0 +1,57 @@
|
||||
# Architecture Decisions
|
||||
|
||||
## ADR-1: Dynamic-only lighting for the demo
|
||||
|
||||
- **Context**: The demo is a fixed daytime scene. Stationary lights demand a lighting build and produce "LIGHTING NEEDS TO BE REBUILT" warnings.
|
||||
|
||||
- **Decision**: `r.AllowStaticLighting=False`, all lights `MOVABLE`.
|
||||
|
||||
- **Consequences**: No bake step, no warning, simpler iteration. Slightly higher GPU cost per frame (no baked shadows) — acceptable for the demo scope.
|
||||
|
||||
## ADR-2: Materials authored in Unreal, not imported from Blender
|
||||
|
||||
- **Context**: FBX→Unreal import maps all Blender diffuse colours to white. Flat colour materials do not survive the pipeline.
|
||||
|
||||
- **Decision**: Build all materials (asphalt, concrete, stucco, façade stone, road paint) directly in Unreal with procedural nodes.
|
||||
|
||||
- **Consequences**: Materials cannot be previewed in Blender look-dev. Must round-trip to Unreal for visual check. But: full PBR control, world-space noise, no import surprise.
|
||||
|
||||
## ADR-3: Complex-as-simple collision on terrain via C++ helper
|
||||
|
||||
- **Context**: UE 5.8 removed `bUseComplexAsSimpleCollision`. Python cannot set `CollisionTraceFlag` on `StaticMesh`/`BodySetup` at all. The Interchange default is bbox-only simple collision, which placed an invisible lid 8 m above the street.
|
||||
|
||||
- **Decision**: `UGigLineAssetTools::SetComplexCollisionAsSimple` (C++) forces `CTF_UseComplexAsSimple` and saves the asset.
|
||||
|
||||
- **Consequences**: One C++ helper class. The flag must be saved on the asset (not just the map). No Python workaround exists.
|
||||
|
||||
## ADR-4: Reset captures StartLocation on first grounded frame
|
||||
|
||||
- **Context**: `BeginPlay` captures `StartLocation` at the PlayerStart, which is above grade (Z 600) so the capsule does not spawn embedded. Every reset dropped the player 1.4 m back into the air.
|
||||
|
||||
- **Decision**: Repeating 0.25 s timer captures `StartLocation` once the character reports `IsMovingOnGround()`, then clears itself.
|
||||
|
||||
- **Consequences**: Reset is correct. A one-shot timer at 2 s is wrong (fires after walking begins, records mid-stride position).
|
||||
|
||||
## ADR-5: Core ticker for QA self-test, not world timer
|
||||
|
||||
- **Context**: Pausing the game stops world timers. A timer-driven test freezes exactly when it proves pause works.
|
||||
|
||||
- **Decision**: `FTSTicker::GetCoreTicker()` drives the `-qaselftest` harness.
|
||||
|
||||
- **Consequences**: Test runs even while paused. Must be enabled explicitly (`-qaselftest`), never in normal play.
|
||||
|
||||
## ADR-6: South = +Y, north = −Y in Unreal
|
||||
|
||||
- **Context**: The street mesh established this convention: `Sidewalk_North` has Y range −1912..−1053, `Sidewalk_South` has +718..+1447.
|
||||
|
||||
- **Decision**: Adopt it project-wide. Never place a building by assumption; always verify against a street mesh or neighbour.
|
||||
|
||||
- **Consequences**: Matches Blender’s north→+Y after the Y-flip in the `ue_metric_v1` export preset.
|
||||
|
||||
## ADR-7: Headless Blender via bpy scripts, not MCP
|
||||
|
||||
- **Context**: Blender skill packs assume an MCP server; the project does not run one.
|
||||
|
||||
- **Decision**: All Blender work driven by Python scripts in `Tools/`. Skill packs used as method guides (checklists, naming, proportions) only.
|
||||
|
||||
- **Consequences**: Scripts are version-controlled and repeatable. No live Blender session needed for CI.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Quality Requirements
|
||||
|
||||
## Quality Requirements Overview
|
||||
|
||||
| Category | Requirement | Priority |
|
||||
| --- | --- | --- |
|
||||
| Performance efficiency | 60 FPS at 2560×1440, documented preset, no frame generation | 1 |
|
||||
| Reliability / Terrain | Zero raycast holes across sidewalks and carriageway (84-sample transect) | 1 |
|
||||
| Accuracy / Fidelity | Building placement matches reference photography within ~1 m | 2 |
|
||||
| Accuracy / Fidelity | Street scale (lane width, sidewalk width) within ~10 cm of measured profile | 2 |
|
||||
| Usability | WASD + mouse look, jump, reset (R), pause (Escape) — no tutorial needed | 2 |
|
||||
| Maintainability | Fresh checkout builds with documented engine installation | 2 |
|
||||
| Portability | Linux build packages and runs; Windows build ships | 3 |
|
||||
|
||||
## Quality Scenarios
|
||||
|
||||
| ID | Source/Stimulus | Response | Metric |
|
||||
| --- | --- | --- | --- |
|
||||
| QS-1 | Player walks both sides of the demo | No fall-through, character stays at ground Z | 0 raycast holes in 84-sample transect |
|
||||
| QS-2 | Player holds W for 2 s on flat road | Character moves forward | ≥ 200 cm displacement, Z constant within ±5 cm |
|
||||
| QS-3 | Player presses R | Character teleports to spawn | < 100 cm from `StartLocation` |
|
||||
| QS-4 | Player presses Escape | Game pauses, cursor appears | `IsGamePaused()` = true |
|
||||
| QS-5 | Player presses Escape while paused | Game unpauses, cursor hides | `IsGamePaused()` = false |
|
||||
| QS-6 | Game runs on dev workstation at 1440p | Frame rate stable | ≥ 60 FPS, 99th percentile ≥ 55 FPS |
|
||||
| QS-7 | Fresh checkout + engine install | Build succeeds | Exit code 0, runnable binary produced |
|
||||
@@ -0,0 +1,10 @@
|
||||
# Risks and Technical Debts
|
||||
|
||||
| ID | Risk / Debt | Priority | Mitigation |
|
||||
| --- | --- | --- | --- |
|
||||
| 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-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. |
|
||||
@@ -0,0 +1,21 @@
|
||||
# Glossary
|
||||
|
||||
| Term | Definition |
|
||||
| --- | --- |
|
||||
| Blosm | Blender add-on (2.7.28) that imports OSM data as 3D building and street blockouts. Used for layout only — not authoritative for façade or height. |
|
||||
| PIE | Play In Editor — an Unreal Editor feature that runs the current map as a live game session inside the editor window, with full game code, input, and physics, without packaging a build. Used for interactive verification (walk, reset, pause) before the scripted QA self-test. |
|
||||
| Complex-as-simple collision | `CTF_UseComplexAsSimple`: Unreal collision mode where the render mesh triangles are used as the collision surface, instead of a simplified proxy. Required for terrain so the player walks on the actual ground shape. |
|
||||
| DEM | Digital Elevation Model. 3DEP 1 m resolution GeoTIFF covering the demo bounds. Source of terrain heightfield. |
|
||||
| El Capitan | El Capitan Entertainment Centre, 6838 Hollywood Blvd. The hero building of the demo. |
|
||||
| Enhanced Input | Unreal’s modern input system (UE 5+). Uses Input Actions (`IA_*`) and Input Mapping Contexts (`IMC_*`) instead of legacy axis/action bindings. |
|
||||
| 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. |
|
||||
| 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. |
|
||||
| Squash merge | Git merge strategy that compresses all branch commits into one commit on `main`, keeping a linear history. |
|
||||
| Swept transect | A QA technique that raycasts vertically at regular intervals along a path (e.g. every 200 cm along both sidewalks and the carriageway) and counts holes (misses). Zero holes = the block is fully walkable. |
|
||||
| UE | Unreal Engine — Epic Games' real-time 3D creation platform (version 5.8.4 in this project). Comprises the Unreal Editor (including PIE, content browser, viewport, blueprint editor), a C++ game framework, and runtime systems for rendering (Vulkan RHI on Linux), physics (Chaos), and input (Enhanced Input). The game project is `GigLine.uproject`; game code is a single C++ module in `Game/GigLine/Source/GigLine/`. |
|
||||
| World Partition | Unreal's world composition system for large open-world levels. Not yet used in `0.1.0` but planned for `0.3.0+` driving routes. |
|
||||
| Y convention | In Unreal, south = +Y, north = −Y (confirmed from street mesh orientation). In Blender, north = +Y; the `ue_metric_v1` export preset flips Y. |
|
||||
@@ -0,0 +1,13 @@
|
||||
**About arc42**
|
||||
|
||||
<div data-wrapper="1" role="lead">
|
||||
|
||||
arc42, the template for documentation of software and system architecture.
|
||||
|
||||
</div>
|
||||
|
||||
Template Version 0.1.0. initial architecture documentation, 2026-11-08
|
||||
|
||||
Created, maintained and © by Dr. Peter Hruschka, Dr. Gernot Starke and contributors. See <https://arc42.org>.
|
||||
|
||||
Adapted for the Gig Line project (Linux-first walking/driving demo around 6840 Hollywood Blvd).
|
||||
@@ -0,0 +1,518 @@
|
||||
# Gig Line Architecture Documentation
|
||||
|
||||
> Linux-first walking/driving demo around 6840 Hollywood Blvd
|
||||
|
||||
Version 0.1.0 — 2026-11-08 — initial architecture documentation
|
||||
|
||||
# Introduction and Goals
|
||||
|
||||
Gig Line is a Linux-first Unreal Engine 5 game that reconstructs a walkable (and eventually drivable) stretch of Hollywood Boulevard around 6840 Hollywood Blvd / El Capitan Entertainment Centre. The initial release (`0.1.0`) delivers a first-person walking demo of the south-side block between Highland Avenue and Orange Drive, plus north-side buildings within 50 m of the frontage.
|
||||
|
||||
## Requirements Overview
|
||||
|
||||
| ID | Description |
|
||||
| --- | --- |
|
||||
| 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-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 |
|
||||
| F-8 | 60 FPS at 1440p on the documented workstation and quality preset |
|
||||
|
||||
Full requirements and progress are in [TODO.md](../../TODO.md); version direction is in [ROADMAP.md](../../ROADMAP.md).
|
||||
|
||||
## Quality Goals
|
||||
|
||||
| Priority | Quality Goal | Scenario |
|
||||
| --- | --- | --- |
|
||||
| 1 | **Performance efficiency** | 60 FPS at 2560×1440 on RTX 5070 Ti, documented preset, no frame generation |
|
||||
| 2 | **Terrain walkability** | Zero raycast holes across both sidewalks and the carriageway (84-sample transect passes) |
|
||||
| 3 | **Geographic fidelity** | Building placement, street scale, and façade identity match reference photography |
|
||||
| 4 | **Build reproducibility** | Fresh checkout + documented engine path produces a runnable Linux build |
|
||||
| 5 | **Provenance** | Every committed asset has recorded provenance |
|
||||
|
||||
## Stakeholders
|
||||
|
||||
| Role | Contact | Expectations |
|
||||
| --- | --- | --- |
|
||||
| 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 |
|
||||
|
||||
# Architecture Constraints
|
||||
|
||||
## Technical Constraints
|
||||
|
||||
| Constraint | Explanation |
|
||||
| --- | --- |
|
||||
| Engine: Unreal 5.8.4 | Pinned; no bundled Linux clang — uses system clang 23.1.4. Game targets use `BuildSettingsVersion.V7`. |
|
||||
| Platform: Linux first | Developed and tested on Bazzite Linux (Fedora 45 base). Windows builds ship but are secondary. |
|
||||
| GPU: RTX 5070 Ti 16 GB | Dev workstation target. Vulkan RHI; open kernel module driver 615.78.08. |
|
||||
| No static lighting | `r.AllowStaticLighting=False`. The demo is a fixed daytime scene lit entirely dynamically to avoid "LIGHTING NEEDS TO BE REBUILT" warnings. |
|
||||
| Blender→UE via FBX | Flat diffuse colours do not survive FBX→Unreal import. All materials are authored in UE. |
|
||||
| Terrain collision | UE 5.8 exposes `CollisionTraceFlag` (not `bUseComplexAsSimpleCollision`); Python cannot set it. Must use C++ helper `UGigLineAssetTools::SetComplexCollisionAsSimple`. |
|
||||
| Coordinate convention | South = +Y, north = −Y (ground truth = street mesh orientation). 1 m in Blender = 100 cm in Unreal. |
|
||||
|
||||
## Organisational Constraints
|
||||
|
||||
| Constraint | Explanation |
|
||||
| --- | --- |
|
||||
| Squash-merge to `main` | Every PR is squash-merged for a linear history. No direct commits to `main` except integration merges. |
|
||||
| Visual review required | Any change affecting appearance must produce screenshots for human approval before being marked done. |
|
||||
| Asset provenance | Every committed asset must have verified licence and redistribution permission recorded in `THIRD_PARTY_NOTICES.md`. |
|
||||
| Reference photography | Local only, gitignored, never committed. Used for façade work under fair-use / internal-development rationale. |
|
||||
| LFS for binaries | `.uasset`, `.umap`, `.blend` tracked with Git LFS; `Binaries/`, `Intermediate/`, `Saved/` are gitignored. |
|
||||
|
||||
## Conventions
|
||||
|
||||
| Convention | Explanation |
|
||||
| --- | --- |
|
||||
| Semantic versioning | `MAJOR.MINOR.PATCH`. `0.x` documents breaking changes. `1.0.0` = agreed game scope. |
|
||||
| Task tracking in `TODO.md` | Single central task list. `- [ ]` open, `- [-]` or `- [~]` in progress, `- [x]` done, `- [A]` active, `- [D]` deferred. |
|
||||
| Headless Blender | All Blender work driven by `bpy` scripts in `Tools/`, not MCP. Blender skills are method guides only. |
|
||||
| Named export presets | `ue_metric_v1` for static meshes, `ue_character_v1` for skeletal. Every exporter calls the metre→centimetre conversion. |
|
||||
|
||||
# Context and Scope
|
||||
|
||||
## Business Context
|
||||
|
||||
The system is a self-contained single-player game. It has no server backend, no multiplayer, and no live service dependencies. Its communication partners are:
|
||||
|
||||
| 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 | — |
|
||||
| Filesystem | Engine content (`.uasset`, `.umap`), Blender sources (`.blend`), DEM GeoTIFF | Saved maps, screenshots, packaged build |
|
||||
|
||||
## Technical Context
|
||||
|
||||
| Channel | Protocol / Format | Purpose |
|
||||
| --- | --- | --- |
|
||||
| Keyboard + mouse | Enhanced Input (IA\_\*, IMC_Default) | First-person movement, look, jump, reset, pause |
|
||||
| Vulkan RHI | GPU rendering | Scene display at 60 FPS / 1440p |
|
||||
| FBX | Autodesk FBX (binary) | Blender→Unreal mesh and skeleton transfer |
|
||||
| GeoTIFF | 3DEP 1 m DEM | Terrain heightfield source |
|
||||
| OSM XML | Overpass API extract | Building footprint and street layout source |
|
||||
| Git / LFS | HTTPS to Gitea remote | Source control, binary asset storage |
|
||||
|
||||
# Solution Strategy
|
||||
|
||||
## Technology decisions
|
||||
|
||||
- **Unreal Engine 5.8.4** for rendering, World Partition, physics (Chaos), and the Enhanced Input system. Chosen for its Linux support, open C++ source, and real-time rendering quality.
|
||||
|
||||
- **Blender 5.2 LTS** for all 3D modelling, UV work, and collision mesh authoring. Driven headlessly via `bpy` Python scripts, not MCP.
|
||||
|
||||
- **QGIS 4.2.3** for geographic data cleanup, coordinate conversion (EPSG:26911), and DEM preparation.
|
||||
|
||||
- **Blosm 2.7.28** for OSM building and street blockouts inside Blender (layout data only — not authoritative façade, height, or pavement detail).
|
||||
|
||||
- **MPFB 2.0.18** for human base meshes and baked materials (NPCs in `0.2.0+`).
|
||||
|
||||
## Top-level decomposition
|
||||
|
||||
The system is a single Unreal game module (`GigLine`) with no plugins beyond `EnhancedInput`. Content is organised by domain under `/Game/Hollywood/`:
|
||||
|
||||
- `Site/` — terrain, road, sidewalks, kerbs, crossing markings, site materials
|
||||
|
||||
- `ElCapitan/` — hero façade mesh and materials
|
||||
|
||||
- `Frontage/` — south-side block building meshes
|
||||
|
||||
- `North/` — north-side building meshes
|
||||
|
||||
- `Input/` — Enhanced Input actions and mapping context
|
||||
|
||||
All modelling and geographic processing live outside the game project in `Art/`, `Geo/`, and `Tools/`. The `Game/` directory contains only engine-ready content and C++ source.
|
||||
|
||||
## Quality achievement
|
||||
|
||||
- **Performance**: dynamic-only lighting (no build step, no static shadow maps), world-space-noise materials (cheap procedural variation), minimal overdraw (simple street-level geometry).
|
||||
|
||||
- **Walkability**: complex-as-simple collision on terrain (C++ helper forces the flag on the asset), block-all collision on every site mesh, swept transect QA verifies zero raycast holes.
|
||||
|
||||
- **Fidelity**: geographic data verified before modelling (footprints, bounds, DEM); building placement checked against street mesh, never in isolation; reference photography drives façade work.
|
||||
|
||||
- **Reproducibility**: pinned toolchain in `Docs/toolchain.md`, named export presets, `Tools/check-lfs.sh` gate, fresh-checkout build procedure in `Docs/build.md`.
|
||||
|
||||
# Building Block View
|
||||
|
||||
## Whitebox Overall System
|
||||
|
||||
The system decomposes into five top-level building blocks:
|
||||
|
||||
| **Name** | **Responsibility** |
|
||||
| --- | --- |
|
||||
| Gameplay | Player pawn, movement, camera, input bindings, HUD, game mode |
|
||||
| Site | Terrain, road, sidewalks, kerbs, crossing markings, site-level collision |
|
||||
| Façades | Hero façade (El Capitan), south block frontage, north-side buildings |
|
||||
| Art Pipeline | Blender models, geographic data, export scripts, import scripts |
|
||||
| QA & Review | Screenshot pipeline, self-test harness, visual review workflow |
|
||||
|
||||
### Gameplay
|
||||
|
||||
C++ classes in `Game/GigLine/Source/GigLine/`:
|
||||
|
||||
| Class | Responsibility | Key detail |
|
||||
| --- | --- | --- |
|
||||
| `AGigLineCharacter` | First-person player pawn | 1.75 m capsule, 4.5 m/s walk, Enhanced Input bindings, reset, pause, `-qaselftest` harness |
|
||||
| `AGigLineGameModeBase` | Sets game mode and HUD class | `HUDClass = AGigLineHUD` |
|
||||
| `AGigLineHUD` | HUD text overlay | `DrawText` every frame, bottom-left |
|
||||
| `AGigLineNPCPerson` | NPC prefab (future) | Capsule + visible MPFB body, feet on Z 0 |
|
||||
| `AGigLineShotActor` | Screenshot camera walker | Drives cameras in `-game` mode, `FScreenshotRequest`, quits |
|
||||
| `UGigLineAssetTools` | C++ collision helpers | `SetComplexCollisionAsSimple` on `StaticMesh` assets |
|
||||
|
||||
Input assets in `/Game/Input/`: `IA_Move`, `IA_MoveBack`, `IA_Strafe`, `IA_StrafeLeft`, `IA_Look`, `IA_Jump`, `IA_Reset`, `IA_Pause`, bound in `IMC_Default`.
|
||||
|
||||
### Site
|
||||
|
||||
Static meshes and materials in `/Game/Hollywood/Site/`:
|
||||
|
||||
| Asset | Role |
|
||||
| --- | --- |
|
||||
| `Terrain_DEM` | Ground heightfield (complex-as-simple collision) |
|
||||
| `Road_Hollywood` | Carriageway surface (block-all collision) |
|
||||
| `Sidewalk_North`, `Sidewalk_South` | Pedestrian surfaces (block-all collision) |
|
||||
| `Kerb_North`, `Kerb_South` | Kerb edges |
|
||||
| `Crossing_Markings` | Zebra markings at signalised crossing |
|
||||
| `M_Asphalt`, `M_Concrete`, `M_KerbConcrete`, `M_Ground`, `M_RoadPaint`, `M_Stucco`, `M_FacadeStone` | Procedural materials (world-space-noise blend + roughness) |
|
||||
|
||||
### Façades
|
||||
|
||||
| Asset | Role |
|
||||
| --- | --- |
|
||||
| `SM_HeroFacade` | El Capitan Entertainment Centre detailed façade |
|
||||
| `SM_ElCapitanTheatre`, `SM_Budget_6822`, `SM_Hollywood_6806`, `SM_Hollywood_6902`, `SM_Hollywood_6922`, `SM_LeeDrug_6800` | South block frontage (6 buildings) |
|
||||
| `SM_Madame_Tussauds`, `SM_TCL_Chinese_Theatre` | North-side buildings (within 50 m) |
|
||||
|
||||
### Art Pipeline
|
||||
|
||||
Python scripts in `Tools/` drive the content pipeline:
|
||||
|
||||
| Script group | Purpose |
|
||||
| --- | --- |
|
||||
| `export_*`, `hero_facade_build` | Blender headless export (FBX) |
|
||||
| `ue_import_*`, `ue_hero_facade_import` | Unreal headless import + placement |
|
||||
| `ue_build_demo_map`, `ue_build_review_map` | Map assembly (lighting, meshes, player start) |
|
||||
| `ue_create_input_assets` | Enhanced Input actions + mapping context |
|
||||
| `render_screenshots.sh` | Screenshot capture pipeline |
|
||||
| `judge_screenshot.py` | Vision-model pre-check (optional) |
|
||||
|
||||
Source data: `Art/blender/` (`.blend` files), `Geo/` (OSM extracts, DEM, verification docs).
|
||||
|
||||
### QA & Review
|
||||
|
||||
- `AGigLineShotActor` walks fixed cameras in `-game` mode, captures PNGs, and quits.
|
||||
|
||||
- `-qaselftest` switch on `AGigLineCharacter` runs a core-ticker harness: walk 2 s, reset, pause/unpause, ground transect — all PASS/FAIL logged.
|
||||
|
||||
- `Screenshots/` holds committed PNGs and `README.md` approval table.
|
||||
|
||||
## Level 2
|
||||
|
||||
### White Box Gameplay — Input Flow
|
||||
|
||||
1. `AGigLineCharacter::SetupPlayerInputComponent` binds `IA_*` actions to `MoveForward`, `MoveBack`, `MoveStrafe`, `MoveLeft`, `Look`, `StartJump`/`StopJump`, `ResetToStart`, `TogglePause`.
|
||||
|
||||
2. Movement delegates to `ApplyMove(Forward, Strafe)` → `AddMovementInput`.
|
||||
|
||||
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.
|
||||
|
||||
5. `TogglePause` toggles `APlayerController::SetPause`, swaps input mode between `FInputModeGameOnly` and `FInputModeUIOnly`, shows/hides mouse cursor.
|
||||
|
||||
### White Box Site — Collision Model
|
||||
|
||||
- Every site mesh (road, sidewalks, kerbs, crossing) is `STATIC` with `BlockAll` collision.
|
||||
|
||||
- `Terrain_DEM` uses `CTF_UseComplexAsSimple` (forced by `UGigLineAssetTools` at build time, saved on the asset).
|
||||
|
||||
- The terrain corridor is carved to half-width 10.7 m around the road centreline so the player can walk the carriageway and both sidewalks.
|
||||
|
||||
- Ground Z: north sidewalk 395, road 366, south sidewalk 317 (3.94 m fall along the block, matching the DEM).
|
||||
|
||||
# Runtime View
|
||||
|
||||
## Walk the block
|
||||
|
||||
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.
|
||||
|
||||
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).
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## QA self-test (`-qaselftest`)
|
||||
|
||||
1. Core ticker starts at `BeginPlay` (not a world timer — world timers freeze on pause).
|
||||
|
||||
2. 1.0 s: begin driving forward input.
|
||||
|
||||
3. 3.0 s: stop, measure displacement (> 200 cm = PASS).
|
||||
|
||||
4. 3.5 s: call `ResetToStart`.
|
||||
|
||||
5. 4.0 s: measure distance from spawn (< 100 cm = PASS).
|
||||
|
||||
6. 4.5 s: call `TogglePause`, check `IsGamePaused()` (= true = 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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
# Deployment View
|
||||
|
||||
## Infrastructure Level 1 — Development Workstation
|
||||
|
||||
| Node | Specification |
|
||||
| --- | --- |
|
||||
| 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) |
|
||||
|
||||
Motivation
|
||||
Single-machine development. No build farm, no CI pipeline yet. The engine, toolchain, and GPU are all on the same host.
|
||||
|
||||
Quality and Performance Features
|
||||
|
||||
- Vulkan RHI on RTX 5070 Ti, open kernel module driver 615.78.08.
|
||||
|
||||
- System clang 23.1.4 (no bundled engine clang) compiles game code.
|
||||
|
||||
- Build time: ~90 s incremental, ~5 min clean for the editor target.
|
||||
|
||||
Mapping of Building Blocks to Infrastructure
|
||||
|
||||
| Building Block | Infrastructure Element |
|
||||
| --- | --- |
|
||||
| Gameplay (C++) | Dev workstation — compiled to `libUnrealEditor-GigLine.so`, loaded by engine |
|
||||
| Site + Façades (UE content) | Dev workstation — `.uasset`/`.umap` in `Game/GigLine/Content/` |
|
||||
| Art Pipeline (Python/Blender) | Dev workstation — headless Blender 5.2 Flatpak, Python 3.14.8 |
|
||||
| QA & Review | Dev workstation — game run in `-game` mode, PNGs to `Screenshots/` |
|
||||
| Git remote | `git.allucanget.biz` — Gitea instance, LFS-enabled |
|
||||
|
||||
## Infrastructure Level 2 — Packaged Build
|
||||
|
||||
| Element | Detail |
|
||||
| --- | --- |
|
||||
| Linux packaged build | Development config, ~1.1 GiB. Contains engine binaries, game module, cooked content, no editor. |
|
||||
| Run command | `./GigLine.sh -windowed -ResX=2560 -ResY=1440` |
|
||||
|
||||
No server infrastructure. The game is a standalone executable.
|
||||
|
||||
# Cross-cutting Concepts
|
||||
|
||||
## Coordinate system and scale
|
||||
|
||||
- **Source CRS**: EPSG:26911 (UTM zone 11N). Local origin recorded in `Geo/`.
|
||||
|
||||
- **Blender**: metres. East → +X, north → +Y (Blender convention).
|
||||
|
||||
- **Unreal**: centimetres. East → +X, north → **−Y** (confirmed from street mesh: `Sidewalk_North` Y −1912..−1053, `Sidewalk_South` Y +718..+1447).
|
||||
|
||||
- **Conversion**: every exporter calls the metre→centimetre function. Import presets (`ue_metric_v1`, `ue_character_v1`) are named and repeatable.
|
||||
|
||||
Rule: place a building only against a neighbour or street mesh, never in isolation.
|
||||
|
||||
## Collision model
|
||||
|
||||
- Site meshes (road, sidewalks, kerbs, crossing): `STATIC` mobility, `BlockAll` collision profile.
|
||||
|
||||
- Terrain: `STATIC`, `CTF_UseComplexAsSimple` (forced by `UGigLineAssetTools` at build time, saved on the asset — the flag lives on the `UStaticMesh`/`UBodySetup`, not the map).
|
||||
|
||||
- Buildings: `STATIC`, simple collision (convex hull from mesh).
|
||||
|
||||
- Rule: the collision flag must be saved on the asset. Saving only the `.umap` silently reverts it.
|
||||
|
||||
## Lighting
|
||||
|
||||
- Dynamic-only: `r.AllowStaticLighting=False`. All lights are `MOVABLE`.
|
||||
|
||||
- Fixed daytime rig: directional light (sun, −62° pitch), sky light, exponential height fog, post-process volume with fixed exposure.
|
||||
|
||||
- Rationale: avoids "LIGHTING NEEDS TO BE REBUILT" warnings; no bake step; the scene never changes time of day.
|
||||
|
||||
## Materials
|
||||
|
||||
- All materials authored in Unreal, not carried from Blender (FBX import loses diffuse colours).
|
||||
|
||||
- World-space-noise blend nodes for cheap procedural variation (asphalt grain, concrete speckle, stucco roughness).
|
||||
|
||||
- Instance parameters where tunable (e.g. wetness, ageing).
|
||||
|
||||
## Geographic data provenance
|
||||
|
||||
- OSM extract is layout data only — not authoritative for façade, height, or pavement detail.
|
||||
|
||||
- DEM from 3DEP 1 m; elevation verified against cross-sections in `Geo/verification/`.
|
||||
|
||||
- Reference photography (local, gitignored) drives manual façade work.
|
||||
|
||||
## QA harness
|
||||
|
||||
- `-qaselftest`: core ticker (not world timer) so pause cannot freeze the test that proves pause works.
|
||||
|
||||
- `GigLine SIM` / `GigLine GROUND`: TEMP diagnostic logging, to be removed before release tag.
|
||||
|
||||
- Screenshot approval: human must tick `Screenshots/README.md` before visual work is marked done.
|
||||
|
||||
# Architecture Decisions
|
||||
|
||||
## ADR-1: Dynamic-only lighting for the demo
|
||||
|
||||
- **Context**: The demo is a fixed daytime scene. Stationary lights demand a lighting build and produce "LIGHTING NEEDS TO BE REBUILT" warnings.
|
||||
|
||||
- **Decision**: `r.AllowStaticLighting=False`, all lights `MOVABLE`.
|
||||
|
||||
- **Consequences**: No bake step, no warning, simpler iteration. Slightly higher GPU cost per frame (no baked shadows) — acceptable for the demo scope.
|
||||
|
||||
## ADR-2: Materials authored in Unreal, not imported from Blender
|
||||
|
||||
- **Context**: FBX→Unreal import maps all Blender diffuse colours to white. Flat colour materials do not survive the pipeline.
|
||||
|
||||
- **Decision**: Build all materials (asphalt, concrete, stucco, façade stone, road paint) directly in Unreal with procedural nodes.
|
||||
|
||||
- **Consequences**: Materials cannot be previewed in Blender look-dev. Must round-trip to Unreal for visual check. But: full PBR control, world-space noise, no import surprise.
|
||||
|
||||
## ADR-3: Complex-as-simple collision on terrain via C++ helper
|
||||
|
||||
- **Context**: UE 5.8 removed `bUseComplexAsSimpleCollision`. Python cannot set `CollisionTraceFlag` on `StaticMesh`/`BodySetup` at all. The Interchange default is bbox-only simple collision, which placed an invisible lid 8 m above the street.
|
||||
|
||||
- **Decision**: `UGigLineAssetTools::SetComplexCollisionAsSimple` (C++) forces `CTF_UseComplexAsSimple` and saves the asset.
|
||||
|
||||
- **Consequences**: One C++ helper class. The flag must be saved on the asset (not just the map). No Python workaround exists.
|
||||
|
||||
## ADR-4: Reset captures StartLocation on first grounded frame
|
||||
|
||||
- **Context**: `BeginPlay` captures `StartLocation` at the PlayerStart, which is above grade (Z 600) so the capsule does not spawn embedded. Every reset dropped the player 1.4 m back into the air.
|
||||
|
||||
- **Decision**: Repeating 0.25 s timer captures `StartLocation` once the character reports `IsMovingOnGround()`, then clears itself.
|
||||
|
||||
- **Consequences**: Reset is correct. A one-shot timer at 2 s is wrong (fires after walking begins, records mid-stride position).
|
||||
|
||||
## ADR-5: Core ticker for QA self-test, not world timer
|
||||
|
||||
- **Context**: Pausing the game stops world timers. A timer-driven test freezes exactly when it proves pause works.
|
||||
|
||||
- **Decision**: `FTSTicker::GetCoreTicker()` drives the `-qaselftest` harness.
|
||||
|
||||
- **Consequences**: Test runs even while paused. Must be enabled explicitly (`-qaselftest`), never in normal play.
|
||||
|
||||
## ADR-6: South = +Y, north = −Y in Unreal
|
||||
|
||||
- **Context**: The street mesh established this convention: `Sidewalk_North` has Y range −1912..−1053, `Sidewalk_South` has +718..+1447.
|
||||
|
||||
- **Decision**: Adopt it project-wide. Never place a building by assumption; always verify against a street mesh or neighbour.
|
||||
|
||||
- **Consequences**: Matches Blender’s north→+Y after the Y-flip in the `ue_metric_v1` export preset.
|
||||
|
||||
## ADR-7: Headless Blender via bpy scripts, not MCP
|
||||
|
||||
- **Context**: Blender skill packs assume an MCP server; the project does not run one.
|
||||
|
||||
- **Decision**: All Blender work driven by Python scripts in `Tools/`. Skill packs used as method guides (checklists, naming, proportions) only.
|
||||
|
||||
- **Consequences**: Scripts are version-controlled and repeatable. No live Blender session needed for CI.
|
||||
|
||||
# Quality Requirements
|
||||
|
||||
## Quality Requirements Overview
|
||||
|
||||
| Category | Requirement | Priority |
|
||||
| --- | --- | --- |
|
||||
| Performance efficiency | 60 FPS at 2560×1440, documented preset, no frame generation | 1 |
|
||||
| Reliability / Terrain | Zero raycast holes across sidewalks and carriageway (84-sample transect) | 1 |
|
||||
| Accuracy / Fidelity | Building placement matches reference photography within ~1 m | 2 |
|
||||
| Accuracy / Fidelity | Street scale (lane width, sidewalk width) within ~10 cm of measured profile | 2 |
|
||||
| Usability | WASD + mouse look, jump, reset (R), pause (Escape) — no tutorial needed | 2 |
|
||||
| Maintainability | Fresh checkout builds with documented engine installation | 2 |
|
||||
| Portability | Linux build packages and runs; Windows build ships | 3 |
|
||||
|
||||
## Quality Scenarios
|
||||
|
||||
| ID | Source/Stimulus | Response | Metric |
|
||||
| --- | --- | --- | --- |
|
||||
| QS-1 | Player walks both sides of the demo | No fall-through, character stays at ground Z | 0 raycast holes in 84-sample transect |
|
||||
| QS-2 | Player holds W for 2 s on flat road | Character moves forward | ≥ 200 cm displacement, Z constant within ±5 cm |
|
||||
| QS-3 | Player presses R | Character teleports to spawn | < 100 cm from `StartLocation` |
|
||||
| QS-4 | Player presses Escape | Game pauses, cursor appears | `IsGamePaused()` = true |
|
||||
| QS-5 | Player presses Escape while paused | Game unpauses, cursor hides | `IsGamePaused()` = false |
|
||||
| QS-6 | Game runs on dev workstation at 1440p | Frame rate stable | ≥ 60 FPS, 99th percentile ≥ 55 FPS |
|
||||
| QS-7 | Fresh checkout + engine install | Build succeeds | Exit code 0, runnable binary produced |
|
||||
|
||||
# Risks and Technical Debts
|
||||
|
||||
| ID | Risk / Debt | Priority | Mitigation |
|
||||
| --- | --- | --- | --- |
|
||||
| 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-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. |
|
||||
|
||||
# Glossary
|
||||
|
||||
| Term | Definition |
|
||||
| --- | --- |
|
||||
| Blosm | Blender add-on (2.7.28) that imports OSM data as 3D building and street blockouts. Used for layout only — not authoritative for façade or height. |
|
||||
| PIE | Play In Editor — an Unreal Editor feature that runs the current map as a live game session inside the editor window, with full game code, input, and physics, without packaging a build. Used for interactive verification (walk, reset, pause) before the scripted QA self-test. |
|
||||
| Complex-as-simple collision | `CTF_UseComplexAsSimple`: Unreal collision mode where the render mesh triangles are used as the collision surface, instead of a simplified proxy. Required for terrain so the player walks on the actual ground shape. |
|
||||
| DEM | Digital Elevation Model. 3DEP 1 m resolution GeoTIFF covering the demo bounds. Source of terrain heightfield. |
|
||||
| El Capitan | El Capitan Entertainment Centre, 6838 Hollywood Blvd. The hero building of the demo. |
|
||||
| Enhanced Input | Unreal’s modern input system (UE 5+). Uses Input Actions (`IA_*`) and Input Mapping Contexts (`IMC_*`) instead of legacy axis/action bindings. |
|
||||
| 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. |
|
||||
| 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. |
|
||||
| Squash merge | Git merge strategy that compresses all branch commits into one commit on `main`, keeping a linear history. |
|
||||
| Swept transect | A QA technique that raycasts vertically at regular intervals along a path (e.g. every 200 cm along both sidewalks and the carriageway) and counts holes (misses). Zero holes = the block is fully walkable. |
|
||||
| UE | Unreal Engine — Epic Games' real-time 3D creation platform (version 5.8.4 in this project). Comprises the Unreal Editor (including PIE, content browser, viewport, blueprint editor), a C++ game framework, and runtime systems for rendering (Vulkan RHI on Linux), physics (Chaos), and input (Enhanced Input). The game project is `GigLine.uproject`; game code is a single C++ module in `Game/GigLine/Source/GigLine/`. |
|
||||
| World Partition | Unreal's world composition system for large open-world levels. Not yet used in `0.1.0` but planned for `0.3.0+` driving routes. |
|
||||
| Y convention | In Unreal, south = +Y, north = −Y (confirmed from street mesh orientation). In Blender, north = +Y; the `ue_metric_v1` export preset flips Y. |
|
||||
|
||||
---
|
||||
|
||||
**About arc42**
|
||||
|
||||
<div data-wrapper="1" role="lead">
|
||||
|
||||
arc42, the template for documentation of software and system architecture.
|
||||
|
||||
</div>
|
||||
|
||||
Template Version 0.1.0. initial architecture documentation, 2026-11-08
|
||||
|
||||
Created, maintained and © by Dr. Peter Hruschka, Dr. Gernot Starke and contributors. See <https://arc42.org>.
|
||||
|
||||
Adapted for the Gig Line project (Linux-first walking/driving demo around 6840 Hollywood Blvd).
|
||||
+23
-1
@@ -46,9 +46,15 @@ Engine/Build/BatchFiles/RunUAT.sh BuildCookRun \
|
||||
-archive -archivedirectory="/run/media/system/DATA/Builds/GigLine" \
|
||||
-unattended
|
||||
# Expected: "BUILD SUCCESSFUL" (~1 min for the blank project; archive ~1.1 GiB)
|
||||
# Launch test: /run/media/system/DATA/Builds/GigLine/Linux/GigLine.sh
|
||||
# Launch test (plain, no -map): /run/media/system/DATA/Builds/GigLine/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.
|
||||
|
||||
## Headless content checks
|
||||
@@ -105,3 +111,19 @@ 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).
|
||||
|
||||
## 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,36 @@
|
||||
# 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,82 @@
|
||||
# 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
|
||||
|
+170
-12
@@ -1,6 +1,6 @@
|
||||
# Release checks
|
||||
|
||||
Every version in `ROADMAP.md` ends with release checks. A version is not tagged until all of its checks pass. If a check fails, do not tag; open a patch task in `ROADMAP.md` and report the failing check in `CHANGELOG.md`.
|
||||
Every version in `TODO.md` ends with release checks, and the scope and goal of each version are in `ROADMAP.md`. A version is not tagged until all of its checks pass. If a check fails, do not tag; open a patch task in `TODO.md` and report the failing check in `CHANGELOG.md`.
|
||||
|
||||
Record results in the table at the end of this file, with the date, the exact build under test, and the evidence (screenshot, log, or command output). A check without recorded evidence counts as failed.
|
||||
|
||||
@@ -30,17 +30,175 @@ 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 `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.
|
||||
(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.**
|
||||
+8
-13
@@ -1,9 +1,9 @@
|
||||
# Setup
|
||||
|
||||
Ordered procedure for bringing the workstation and the project to a buildable state. Status markers follow `ROADMAP.md`:
|
||||
Ordered procedure for bringing the workstation and the project to a buildable state. Status markers follow `TODO.md`:
|
||||
|
||||
- `[x]` done and verified
|
||||
- `[-]` started, not finished
|
||||
- `[A]` started, not finished
|
||||
- `[ ]` not started
|
||||
|
||||
Anything marked **human step** needs credentials, licence acceptance, or a decision that cannot be automated. Never store Epic credentials, tokens, or licence keys in the repository.
|
||||
@@ -21,25 +21,20 @@ 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 board is [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
|
||||
|
||||
- `[x]` Update Bazzite with its supported mechanism.
|
||||
`ujust update` (or `rpm-ostree upgrade`) followed by a reboot. Deployment `45.20261105` is booted and default; `44.20261026` retained as rollback.
|
||||
- `[x]` Verify the NVIDIA driver and Vulkan from the desktop session.
|
||||
`nvidia-smi` names the RTX 5070 Ti on driver `615.78.08` and the Wayland desktop renders through it. Vulkan enumerates it as GPU0: discrete, proprietary driver `615.78.08`, API `1.4.351`, conformance `1.4.3.3`. The integrated RADV GPU is GPU1 — watch that the engine does not select it.
|
||||
- `[x]` Install `git-lfs`, `cmake`, `ninja-build`, `clang` as an ostree layer, reboot, then `git lfs install`.
|
||||
Done: `git-lfs 3.7.1`, `clang 23.1.4`, `cmake 4.3.0`, `ninja 1.13.2`; lfs filter active, `Tools/check-lfs.sh` passes.
|
||||
- `[x]` Update Bazzite with its supported mechanism. `ujust update` (or `rpm-ostree upgrade`) followed by a reboot. Deployment `45.20261105` is booted and default; `44.20261026` retained as rollback.
|
||||
- `[x]` Verify the NVIDIA driver and Vulkan from the desktop session. `nvidia-smi` names the RTX 5070 Ti on driver `615.78.08` and the Wayland desktop renders through it. Vulkan enumerates it as GPU0: discrete, proprietary driver `615.78.08`, API `1.4.351`, conformance `1.4.3.3`. The integrated RADV GPU is GPU1 — watch that the engine does not select it.
|
||||
- `[x]` Install `git-lfs`, `cmake`, `ninja-build`, `clang` as an ostree layer, reboot, then `git lfs install`. Done: `git-lfs 3.7.1`, `clang 23.1.4`, `cmake 4.3.0`, `ninja 1.13.2`; lfs filter active, `Tools/check-lfs.sh` passes.
|
||||
|
||||
## 2. Engine acquisition
|
||||
|
||||
- `[x]` **Human step.** Create an Epic account, accept the Unreal license.
|
||||
Done on 2026-11-06 with the account email recorded locally; no credentials stored in the repository.
|
||||
- `[x]` **Human step.** Create an Epic account, accept the Unreal license. Done on 2026-11-06 with the account email recorded locally; no credentials stored in the repository.
|
||||
- `[x]` **Human step.** Acquire one pinned engine build. Two routes:
|
||||
- **Launcher build**: install the Linux Epic Games Launcher, download the pinned engine version, install to the DATA volume, and disable auto-update so the pin holds.
|
||||
- **Source build**: clone the pinned `release` branch from the Epic Games GitHub organisation, then `./Setup.sh`, `./GenerateProjectFiles.sh`, `make -j16`. Budget 120–200 GB and a multi-hour first build on 16 threads.
|
||||
**5.8.4** downloaded to `/run/media/system/DATA/Downloads/Linux_Unreal_Engine_5.8.4.zip`: 39 819 212 076 bytes (37.08 GiB), ZIP central directory present with 299 959 entries, `Engine/Build/Build.version` reports **5.8.4**, changelist 58902052, `++UE5+Release-5.8`.
|
||||
- **Source build**: clone the pinned `release` branch from the Epic Games GitHub organisation, then `./Setup.sh`, `./GenerateProjectFiles.sh`, `make -j16`. Budget 120–200 GB and a multi-hour first build on 16 threads. **5.8.4** downloaded to `/run/media/system/DATA/Downloads/Linux_Unreal_Engine_5.8.4.zip`: 39 819 212 076 bytes (37.08 GiB), ZIP central directory present with 299 959 entries, `Engine/Build/Build.version` reports **5.8.4**, changelist 58902052, `++UE5+Release-5.8`.
|
||||
- `[x]` Verify the archive end to end before extracting. `unzip -tq` on 2026-11-06: "No errors detected", exit 0.
|
||||
- `[x]` Extract to the DATA volume and record the install path. Done 2026-11-07: `/run/media/system/DATA/Engines/UE_5.8.4`, 73 GiB on disk, extraction verified by full headless editor start.
|
||||
- `[x]` Resolve the engine version: **5.8.4 pinned** (see the decision below). The 5.2.0 archive (19.59 GiB, CL 25360045) remains on disk as a fallback only and should be deleted once the pin is trusted.
|
||||
|
||||
@@ -1,8 +1,24 @@
|
||||
[/Script/EngineSettings.GameMapsSettings]
|
||||
GlobalDefaultGameMode="/Script/GigLine.GigLineGameModeBase"
|
||||
; The runnable map is the walkable demo, so a plain launch opens it (not the
|
||||
; engine template nor the review staging scene).
|
||||
GameDefaultMap=/Game/Hollywood/DemoMap
|
||||
EditorStartupMap=/Game/Hollywood/DemoMap
|
||||
ServerDefaultMap=/Game/Hollywood/DemoMap
|
||||
TransitionMap=/Game/Hollywood/DemoMap
|
||||
|
||||
[/Script/Engine.RendererSettings]
|
||||
; Leave defaults for 0.1.0; quality preset is documented at release time.
|
||||
; Dynamic-only lighting. With static lighting allowed, the stationary sun and
|
||||
; sky light demanded a baked lighting build and the editor warned
|
||||
; "LIGHTING NEEDS TO BE REBUILT" (Build Lighting Only / BuildLighting) on every
|
||||
; map. The demo is a fixed daytime scene lit entirely dynamically, so disable
|
||||
; 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
|
||||
@@ -10,7 +26,9 @@ GameName=GigLine
|
||||
[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings]
|
||||
bEnablePlugin=False
|
||||
bAllowNetworkConnection=False
|
||||
SecurityToken=01A1B5CDAFDE7AAEB5DFBB48880D25DE
|
||||
; SecurityToken intentionally empty: it is a credential, and the plugin is
|
||||
; disabled and excluded from shipping. Never commit a real token here.
|
||||
SecurityToken=
|
||||
bIncludeInShipping=False
|
||||
bAllowExternalStartInShipping=False
|
||||
bCompileAFSProject=False
|
||||
|
||||
@@ -7,9 +7,14 @@ CopyrightNotice=
|
||||
BuildConfiguration=PPBC_Development
|
||||
FullRebuild=False
|
||||
UsePakFile=True
|
||||
; Runtime-loaded content must ship even when nothing statically references it.
|
||||
; The Enhanced Input assets are only soft-loaded by AGigLineCharacter at
|
||||
; BeginPlay; without this the cooker prunes them from the pak (observed: the
|
||||
; cook ReferencedSet had zero /Game/Input entries, so inputs were all nil and
|
||||
; no keys worked). Always cook the demo map + input. Keep both maps listed so
|
||||
; EditorStartupMap/GameDefaultMap selection stays flexible.
|
||||
+DirectoriesToAlwaysCook=/Game/Input
|
||||
+DirectoriesToAlwaysCook=/Game/Hollywood
|
||||
+DirectoriesToAlwaysCook=/Game/Test
|
||||
|
||||
|
||||
[/Script/EngineSettings.GameMapsSettings]
|
||||
; Temporary default while the demo map is being built; GameMapsSettings in
|
||||
; DefaultEngine.ini still names the game mode only.
|
||||
GameDefaultMap=/Game/Test/ReviewMap
|
||||
EditorStartupMap=/Game/Test/ReviewMap
|
||||
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.
@@ -0,0 +1,66 @@
|
||||
#include "GigLineAssetTools.h"
|
||||
|
||||
#include "Engine/StaticMesh.h"
|
||||
#include "PhysicsEngine/BodySetup.h"
|
||||
#include "StaticMeshResources.h"
|
||||
|
||||
void UGigLineAssetTools::SetComplexCollisionAsSimple(UStaticMesh* Mesh, bool bEnable)
|
||||
{
|
||||
if (!Mesh)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UBodySetup* BodySetup = Mesh->GetBodySetup();
|
||||
if (!BodySetup)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BodySetup->CollisionTraceFlag = bEnable ? CTF_UseComplexAsSimple : CTF_UseSimpleAndComplex;
|
||||
BodySetup->InvalidatePhysicsData();
|
||||
BodySetup->CreatePhysicsMeshes();
|
||||
Mesh->MarkPackageDirty();
|
||||
// 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)
|
||||
{
|
||||
if (!Mesh)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UBodySetup* BodySetup = Mesh->GetBodySetup();
|
||||
if (!BodySetup)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BodySetup->AggGeom.EmptyElements();
|
||||
BodySetup->InvalidatePhysicsData();
|
||||
BodySetup->CreatePhysicsMeshes();
|
||||
Mesh->MarkPackageDirty();
|
||||
}
|
||||
|
||||
FString UGigLineAssetTools::GetCollisionComplexity(UStaticMesh* Mesh)
|
||||
{
|
||||
if (!Mesh)
|
||||
{
|
||||
return TEXT("no mesh");
|
||||
}
|
||||
|
||||
const UBodySetup* BodySetup = Mesh->GetBodySetup();
|
||||
if (!BodySetup)
|
||||
{
|
||||
return TEXT("no body setup");
|
||||
}
|
||||
|
||||
const int32 Flag = static_cast<int32>(BodySetup->CollisionTraceFlag.GetValue());
|
||||
if (Flag == CTF_UseComplexAsSimple) { return TEXT("UseComplexAsSimple"); }
|
||||
if (Flag == CTF_UseSimpleAsComplex) { return TEXT("UseSimpleAsComplex"); }
|
||||
if (Flag == CTF_UseSimpleAndComplex) { return TEXT("UseSimpleAndComplex"); }
|
||||
return TEXT("ProjectDefault");
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "GigLineAssetTools.generated.h"
|
||||
|
||||
class UStaticMesh;
|
||||
|
||||
/**
|
||||
* Small editor-side helpers for the headless asset pipeline scripts.
|
||||
*
|
||||
* Exposed to Python so tools can fix up imported meshes that the Interchange
|
||||
* importer gives unusable default collision. The terrain is the motivating
|
||||
* case: without complex-as-simple it gets a bounding-box proxy, so the whole
|
||||
* street sits under an invisible 8 m box and the player walks on the box lid.
|
||||
*/
|
||||
UCLASS()
|
||||
class GIGLINE_API UGigLineAssetTools : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Make collision trace against the mesh triangles instead of a proxy hull. */
|
||||
UFUNCTION(BlueprintCallable, Category = "GigLine|Assets")
|
||||
static void SetComplexCollisionAsSimple(UStaticMesh* Mesh, bool bEnable = true);
|
||||
|
||||
/** Drop any generated simple collision (hulls/boxes) from the mesh. */
|
||||
UFUNCTION(BlueprintCallable, Category = "GigLine|Assets")
|
||||
static void ClearSimpleCollision(UStaticMesh* Mesh);
|
||||
|
||||
/** Report the current collision complexity: "SimpleAndComplex", "Simple", ... */
|
||||
UFUNCTION(BlueprintCallable, Category = "GigLine|Assets")
|
||||
static FString GetCollisionComplexity(UStaticMesh* Mesh);
|
||||
};
|
||||
@@ -7,8 +7,24 @@
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "InputAction.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "InputMappingContext.h"
|
||||
#include "Engine/World.h"
|
||||
#include "TimerManager.h"
|
||||
#include "Containers/Ticker.h"
|
||||
#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()
|
||||
{
|
||||
@@ -39,51 +55,56 @@ AGigLineCharacter::AGigLineCharacter()
|
||||
GetMesh()->SetHiddenInGame(true);
|
||||
GetMesh()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
||||
|
||||
// Input assets are created by Tools/ue_create_input_assets.py. They are loaded in
|
||||
// BeginPlay rather than the constructor: the constructor runs during CDO
|
||||
// construction where asset loads are unreliable, and a missing asset should be
|
||||
// a loud warning, not a silent no-op.
|
||||
}
|
||||
// Hard-reference the Enhanced Input assets with ConstructorHelpers so the
|
||||
// cooker always includes them and they load during CDO construction. Runtime
|
||||
// soft-loading them at BeginPlay was fragile: the assets were pruned from the
|
||||
// pak unless /Game/Input was force-cooked, and a soft path can miss while the
|
||||
// IoStore registry is still mounting. A constructor FObjectFinder is a hard
|
||||
// reference (followed by the cooker) and loads deterministically.
|
||||
// Assets are created by Tools/ue_create_input_assets.py.
|
||||
static ConstructorHelpers::FObjectFinder<UInputMappingContext> CtxFinder(
|
||||
TEXT("/Game/Input/IMC_Default.IMC_Default"));
|
||||
static ConstructorHelpers::FObjectFinder<UInputAction> MoveF(
|
||||
TEXT("/Game/Input/IA_Move.IA_Move"));
|
||||
static ConstructorHelpers::FObjectFinder<UInputAction> StrafeF(
|
||||
TEXT("/Game/Input/IA_Strafe.IA_Strafe"));
|
||||
static ConstructorHelpers::FObjectFinder<UInputAction> BackF(
|
||||
TEXT("/Game/Input/IA_MoveBack.IA_MoveBack"));
|
||||
static ConstructorHelpers::FObjectFinder<UInputAction> LeftF(
|
||||
TEXT("/Game/Input/IA_StrafeLeft.IA_StrafeLeft"));
|
||||
static ConstructorHelpers::FObjectFinder<UInputAction> LookF(
|
||||
TEXT("/Game/Input/IA_Look.IA_Look"));
|
||||
static ConstructorHelpers::FObjectFinder<UInputAction> JumpF(
|
||||
TEXT("/Game/Input/IA_Jump.IA_Jump"));
|
||||
static ConstructorHelpers::FObjectFinder<UInputAction> ResetF(
|
||||
TEXT("/Game/Input/IA_Reset.IA_Reset"));
|
||||
static ConstructorHelpers::FObjectFinder<UInputAction> PauseF(
|
||||
TEXT("/Game/Input/IA_Pause.IA_Pause"));
|
||||
|
||||
void AGigLineCharacter::LoadInputAssets()
|
||||
{
|
||||
auto LoadSoft = [](const TCHAR* Path) -> TObjectPtr<UObject>
|
||||
{
|
||||
return LoadObject<UObject>(nullptr, Path);
|
||||
};
|
||||
MappingContext = Cast<UInputMappingContext>(LoadSoft(TEXT("/Game/Input/IMC_Default.IMC_Default")));
|
||||
MoveAction = Cast<UInputAction>(LoadSoft(TEXT("/Game/Input/IA_Move.IA_Move")));
|
||||
StrafeAction = Cast<UInputAction>(LoadSoft(TEXT("/Game/Input/IA_Strafe.IA_Strafe")));
|
||||
MoveBackAction = Cast<UInputAction>(LoadSoft(TEXT("/Game/Input/IA_MoveBack.IA_MoveBack")));
|
||||
StrafeLeftAction = Cast<UInputAction>(LoadSoft(TEXT("/Game/Input/IA_StrafeLeft.IA_StrafeLeft")));
|
||||
LookAction = Cast<UInputAction>(LoadSoft(TEXT("/Game/Input/IA_Look.IA_Look")));
|
||||
JumpAction = Cast<UInputAction>(LoadSoft(TEXT("/Game/Input/IA_Jump.IA_Jump")));
|
||||
ResetAction = Cast<UInputAction>(LoadSoft(TEXT("/Game/Input/IA_Reset.IA_Reset")));
|
||||
PauseAction = Cast<UInputAction>(LoadSoft(TEXT("/Game/Input/IA_Pause.IA_Pause")));
|
||||
MappingContext = CtxFinder.Object;
|
||||
MoveAction = MoveF.Object;
|
||||
StrafeAction = StrafeF.Object;
|
||||
MoveBackAction = BackF.Object;
|
||||
StrafeLeftAction = LeftF.Object;
|
||||
LookAction = LookF.Object;
|
||||
JumpAction = JumpF.Object;
|
||||
ResetAction = ResetF.Object;
|
||||
PauseAction = PauseF.Object;
|
||||
|
||||
if (!MappingContext || !MoveAction || !MoveBackAction || !StrafeAction || !StrafeLeftAction
|
||||
|| !LookAction)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning,
|
||||
TEXT("GigLine: input assets missing (context=%s move=%s look=%s). ")
|
||||
TEXT("Run Tools/ue_create_input_assets.py to build /Game/Input."),
|
||||
MappingContext ? TEXT("ok") : TEXT("missing"),
|
||||
(MoveAction && MoveBackAction && StrafeAction && StrafeLeftAction) ? TEXT("ok")
|
||||
: TEXT("missing"),
|
||||
LookAction ? TEXT("ok") : TEXT("missing"));
|
||||
TEXT("GigLine: input assets failed to load at construction. ")
|
||||
TEXT("Run Tools/ue_create_input_assets.py to build /Game/Input."));
|
||||
}
|
||||
}
|
||||
|
||||
void AGigLineCharacter::BeginPlay()
|
||||
void AGigLineCharacter::LoadInputAssets()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
LoadInputAssets();
|
||||
|
||||
StartLocation = GetActorLocation();
|
||||
StartRotation = GetActorRotation();
|
||||
|
||||
// Push the mapping context for this local player.
|
||||
// The assets are hard-referenced and loaded in the constructor. This pushes
|
||||
// the now-valid mapping context to the local player's subsystem; called from
|
||||
// SetupPlayerInputComponent at possession, when the controller exists.
|
||||
if (MappingContext)
|
||||
{
|
||||
if (const APlayerController* PC = Cast<APlayerController>(GetController()))
|
||||
@@ -92,11 +113,326 @@ void AGigLineCharacter::BeginPlay()
|
||||
ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
|
||||
{
|
||||
Subsystem->AddMappingContext(MappingContext, 0);
|
||||
if (CVarGigLineSetupLogs.GetValueOnGameThread())
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine IN: pushed mapping context %s"),
|
||||
*MappingContext->GetName());
|
||||
}
|
||||
}
|
||||
else if (CVarGigLineSetupLogs.GetValueOnGameThread())
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine IN: no enhanced input subsystem on local player"));
|
||||
}
|
||||
}
|
||||
else if (CVarGigLineSetupLogs.GetValueOnGameThread())
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine IN: no PlayerController at LoadInputAssets"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AGigLineCharacter::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
StartLocation = GetActorLocation();
|
||||
StartRotation = GetActorRotation();
|
||||
|
||||
// The PlayerStart sits above grade so the capsule cannot spawn embedded, so
|
||||
// the BeginPlay position is mid-air. Re-capture once the pawn has landed,
|
||||
// otherwise Reset drops the player 1.4 m back into the air every press.
|
||||
// Repeat until the first grounded frame. A single late timer is wrong: it can
|
||||
// fire after the player (or the QA self-test) has already started walking,
|
||||
// which recorded a mid-stride position as "start".
|
||||
GetWorldTimerManager().SetTimer(SettleHandle, [this]()
|
||||
{
|
||||
if (bQATestWalking)
|
||||
{
|
||||
return; // keep waiting; the test walks almost immediately
|
||||
}
|
||||
if (const UCharacterMovementComponent* Mov = GetCharacterMovement())
|
||||
{
|
||||
if (Mov->IsMovingOnGround())
|
||||
{
|
||||
StartLocation = GetActorLocation();
|
||||
StartRotation = GetActorRotation();
|
||||
GetWorldTimerManager().ClearTimer(this->SettleHandle);
|
||||
if (CVarGigLineSetupLogs.GetValueOnGameThread())
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine: start captured on ground at Z %.0f"),
|
||||
StartLocation.Z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 0.25f, true);
|
||||
|
||||
// Force game input mode + focus + mouse capture so keyboard/mouse reach the
|
||||
// pawn regardless of window focus quirks.
|
||||
if (APlayerController* PC = Cast<APlayerController>(GetController()))
|
||||
{
|
||||
PC->SetInputMode(FInputModeGameOnly());
|
||||
PC->bShowMouseCursor = false;
|
||||
}
|
||||
|
||||
if (FParse::Param(FCommandLine::Get(), TEXT("qaselftest")))
|
||||
{
|
||||
StartQASelfTest();
|
||||
}
|
||||
}
|
||||
|
||||
void AGigLineCharacter::StartQASelfTest()
|
||||
{
|
||||
// Driven by a core ticker rather than a world timer on purpose: pausing the
|
||||
// game stops world timers, so a timer-based test would freeze the moment it
|
||||
// proved pause works.
|
||||
struct FQAState
|
||||
{
|
||||
float Elapsed = 0.0f;
|
||||
FVector Start = FVector::ZeroVector;
|
||||
FVector WalkEnd = FVector::ZeroVector;
|
||||
float Step = 0.0f;
|
||||
bool bWalkMoved = false;
|
||||
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>();
|
||||
State->Start = GetActorLocation();
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine QA: start XYZ=(%.0f,%.0f,%.0f)"),
|
||||
State->Start.X, State->Start.Y, State->Start.Z);
|
||||
|
||||
FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateWeakLambda(this,
|
||||
[this, State](float DeltaTime)
|
||||
{
|
||||
State->Elapsed += DeltaTime;
|
||||
const float E = State->Elapsed;
|
||||
|
||||
// 1.0 - 3.0 s: hold forward. Reset sets the Step marker so each phase
|
||||
// runs exactly once without an ever-growing if-chain.
|
||||
if (E >= 1.0f && State->Step < 1.0f)
|
||||
{
|
||||
State->Step = 1.0f;
|
||||
// Measure from the settled position, not the mid-air spawn point.
|
||||
State->Start = GetActorLocation();
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine QA: settled XYZ=(%.0f,%.0f,%.0f)"),
|
||||
State->Start.X, State->Start.Y, State->Start.Z);
|
||||
bQATestWalking = true;
|
||||
}
|
||||
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.
|
||||
if (E >= 3.0f && State->Step < 2.0f)
|
||||
{
|
||||
State->Step = 2.0f;
|
||||
bQATestWalking = false;
|
||||
State->WalkEnd = GetActorLocation();
|
||||
const float Walked = FVector::Dist2D(State->Start, State->WalkEnd);
|
||||
State->bWalkMoved = Walked > 200.0f; // >2 m in 2 s at 4 m/s
|
||||
UE_LOG(LogTemp, Log,
|
||||
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.
|
||||
if (E >= 3.5f && State->Step < 3.0f)
|
||||
{
|
||||
State->Step = 3.0f;
|
||||
ResetToStart();
|
||||
}
|
||||
if (E >= 4.0f && State->Step < 4.0f)
|
||||
{
|
||||
State->Step = 4.0f;
|
||||
const float Back = FVector::Dist(State->Start, GetActorLocation());
|
||||
State->bResetReturned = Back < 100.0f;
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine QA: reset returned %.0f cm from spawn -> %s"),
|
||||
Back, State->bResetReturned ? TEXT("PASS") : TEXT("FAIL"));
|
||||
}
|
||||
|
||||
// 4.5: pause, then confirm the world actually stopped.
|
||||
if (E >= 4.5f && State->Step < 5.0f)
|
||||
{
|
||||
State->Step = 5.0f;
|
||||
TogglePause();
|
||||
State->bPausedOn = UGameplayStatics::IsGamePaused(this);
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine QA: pause -> paused=%d %s"),
|
||||
State->bPausedOn ? 1 : 0, State->bPausedOn ? TEXT("PASS") : TEXT("FAIL"));
|
||||
}
|
||||
|
||||
// 5.5: unpause and confirm it comes back.
|
||||
if (E >= 5.5f && State->Step < 6.0f)
|
||||
{
|
||||
State->Step = 6.0f;
|
||||
TogglePause();
|
||||
State->bUnpausedBack = !UGameplayStatics::IsGamePaused(this);
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine QA: unpause -> paused=%d %s"),
|
||||
UGameplayStatics::IsGamePaused(this) ? 1 : 0,
|
||||
State->bUnpausedBack ? TEXT("PASS") : TEXT("FAIL"));
|
||||
}
|
||||
|
||||
// 6.0: ground transect - sweep both sidewalks and the carriageway and
|
||||
// count holes, so "can traverse the block without falling through"
|
||||
// is measured rather than assumed.
|
||||
if (E >= 6.0f && State->Step < 7.0f)
|
||||
{
|
||||
State->Step = 7.0f;
|
||||
UWorld* W = GetWorld();
|
||||
int32 Holes = 0, Samples = 0;
|
||||
float Lowest = 1.0e9f, Highest = -1.0e9f;
|
||||
// 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 = -20000.0f; X <= -2000.0f; X += 200.0f)
|
||||
{
|
||||
FHitResult Hit;
|
||||
FCollisionQueryParams Q(TEXT("GigLineTransect"), false, this);
|
||||
const bool bHit = W->LineTraceSingleByChannel(Hit,
|
||||
FVector(X, LaneY, 3000.f), FVector(X, LaneY, -3000.f),
|
||||
ECC_Visibility, Q);
|
||||
++Samples;
|
||||
if (!bHit)
|
||||
{
|
||||
++Holes;
|
||||
continue;
|
||||
}
|
||||
Lowest = FMath::Min(Lowest, Hit.ImpactPoint.Z);
|
||||
Highest = FMath::Max(Highest, Hit.ImpactPoint.Z);
|
||||
}
|
||||
}
|
||||
UE_LOG(LogTemp, Log,
|
||||
TEXT("GigLine QA: transect %d samples, %d holes, ground Z %.0f..%.0f -> %s"),
|
||||
Samples, Holes, Lowest, Highest, Holes == 0 ? TEXT("PASS") : TEXT("FAIL"));
|
||||
|
||||
// Invisible-wall / blocking-mesh probe: at each sample, first find
|
||||
// the ground under the start point (vertical raycast), then run a
|
||||
// horizontal trace 160 cm ABOVE that ground along the street row.
|
||||
// A clean street should let the 30 m trace run clear; a short hit
|
||||
// flags an invisible or misplaced blocker. Starting the horizontal
|
||||
// trace at a fixed Z (e.g. 160) is wrong: the street is at Z ~366
|
||||
// there, so a 160 cm probe starts inside the earth (dist 0).
|
||||
int32 Walls = 0, WallSamples = 0;
|
||||
static const float TraceYs[] = {-1400.0f, -167.0f, 1000.0f, -1382.0f, 1047.0f};
|
||||
for (float WallY : TraceYs)
|
||||
{
|
||||
for (float X0 = -19500.0f; X0 <= -3000.0f; X0 += 2500.0f)
|
||||
{
|
||||
FHitResult GroundHit;
|
||||
FCollisionQueryParams GQ(TEXT("GigLineWallGround"), false, this);
|
||||
if (!W->LineTraceSingleByChannel(GroundHit,
|
||||
FVector(X0, WallY, 3000.f), FVector(X0, WallY, -1000.f),
|
||||
ECC_Visibility, GQ))
|
||||
{
|
||||
continue; // no ground under start -> skip
|
||||
}
|
||||
const float ProbeZ = GroundHit.ImpactPoint.Z + 160.f;
|
||||
FHitResult Hit;
|
||||
FCollisionQueryParams Q(TEXT("GigLineWallProbe"), false, this);
|
||||
const bool bHit = W->LineTraceSingleByChannel(Hit,
|
||||
FVector(X0, WallY, ProbeZ),
|
||||
FVector(X0 + 3000.f, WallY, ProbeZ),
|
||||
ECC_Visibility, Q);
|
||||
++WallSamples;
|
||||
if (!bHit) { continue; } // 30 m clear -> good
|
||||
// Starting just above the ground, a hit within 21 m = blocker.
|
||||
if (Hit.Distance < 2100.f)
|
||||
{
|
||||
++Walls;
|
||||
UE_LOG(LogTemp, Log,
|
||||
TEXT("GigLine QA: wall probe short-hit at X %.0f Y %.0f dist %.0f actor=%s"),
|
||||
X0, WallY, Hit.Distance,
|
||||
Hit.GetActor() ? *Hit.GetActor()->GetName() : TEXT("none"));
|
||||
}
|
||||
}
|
||||
}
|
||||
UE_LOG(LogTemp, Log,
|
||||
TEXT("GigLine QA: wall probe %d/%d traces with blocking hit inside 21 m -> %s"),
|
||||
Walls, WallSamples,
|
||||
Walls == 0 ? TEXT("PASS") : TEXT("FAIL"));
|
||||
}
|
||||
|
||||
// 7.0: verdict, then stop ticking.
|
||||
if (E >= 7.0f)
|
||||
{
|
||||
const bool bAllPass = State->bWalkMoved && State->bResetReturned
|
||||
&& State->bPausedOn && State->bUnpausedBack;
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine QA: RESULT walk=%d reset=%d pause=%d unpause=%d -> %s"),
|
||||
State->bWalkMoved ? 1 : 0, State->bResetReturned ? 1 : 0,
|
||||
State->bPausedOn ? 1 : 0, State->bUnpausedBack ? 1 : 0,
|
||||
bAllPass ? TEXT("PASS") : TEXT("FAIL"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
void AGigLineCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
|
||||
{
|
||||
Super::SetupPlayerInputComponent(PlayerInputComponent);
|
||||
@@ -108,6 +444,10 @@ void AGigLineCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputCo
|
||||
return;
|
||||
}
|
||||
|
||||
// Push the mapping context for this controller's local player so the bound
|
||||
// actions receive input (runs at possession, when the controller exists).
|
||||
LoadInputAssets();
|
||||
|
||||
// One action per direction keeps the sign explicit: W and S differ only by
|
||||
// which handler runs, not by a modifier in the asset.
|
||||
if (MoveAction)
|
||||
@@ -143,6 +483,15 @@ void AGigLineCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputCo
|
||||
Input->BindAction(JumpAction, ETriggerEvent::Started, this, &AGigLineCharacter::StartJump);
|
||||
Input->BindAction(JumpAction, ETriggerEvent::Completed, this, &AGigLineCharacter::StopJump);
|
||||
}
|
||||
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
|
||||
@@ -190,11 +539,14 @@ void AGigLineCharacter::MoveLeft(const FInputActionValue& Value)
|
||||
void AGigLineCharacter::ApplyMove(float Forward, float Strafe)
|
||||
{
|
||||
const AController* Controller = GetController();
|
||||
if (!Controller || (FMath::IsNearlyZero(Forward) && FMath::IsNearlyZero(Strafe)))
|
||||
if (!Controller)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (FMath::IsNearlyZero(Forward) && FMath::IsNearlyZero(Strafe))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Forward/right on the controller yaw keeps the capsule upright on slopes.
|
||||
const FRotator YawRotation(0.0f, Controller->GetControlRotation().Yaw, 0.0f);
|
||||
const FVector ForwardDir = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
|
||||
|
||||
@@ -38,6 +38,22 @@ protected:
|
||||
void ResetToStart();
|
||||
void TogglePause();
|
||||
|
||||
/**
|
||||
* TEMP QA: scripted walk / reset / pause verification.
|
||||
*
|
||||
* Enabled only by the `-qaselftest` command-line switch, so it never runs in
|
||||
* normal play. It exists because those three behaviours were listed as
|
||||
* "needs a human at the keyboard" and a repeatable headless check beats
|
||||
* waiting for one.
|
||||
*/
|
||||
void StartQASelfTest();
|
||||
|
||||
/** Set while the self-test is driving forward input. */
|
||||
bool bQATestWalking = false;
|
||||
|
||||
/** Repeating timer that captures StartLocation on first ground contact. */
|
||||
FTimerHandle SettleHandle;
|
||||
|
||||
/** Where the player started, so reset returns there rather than the origin. */
|
||||
UPROPERTY()
|
||||
FVector StartLocation = FVector::ZeroVector;
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
#include "GigLineGameModeBase.h"
|
||||
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "GigLineCharacter.h"
|
||||
#include "GigLineHUD.h"
|
||||
|
||||
AGigLineGameModeBase::AGigLineGameModeBase()
|
||||
{
|
||||
DefaultPawnClass = AGigLineCharacter::StaticClass();
|
||||
// ODbL attribution must be visible in the running demo, not only in docs.
|
||||
HUDClass = AGigLineHUD::StaticClass();
|
||||
}
|
||||
|
||||
void AGigLineGameModeBase::HandleStartingNewPlayer_Implementation(APlayerController* NewPlayer)
|
||||
{
|
||||
Super::HandleStartingNewPlayer_Implementation(NewPlayer);
|
||||
UE_LOG(LogTemp, Log, TEXT("GigLine GM: player %s connected; pawn=%s input=%s"),
|
||||
NewPlayer ? *NewPlayer->GetName() : TEXT("null"),
|
||||
NewPlayer && NewPlayer->GetPawn() ? *NewPlayer->GetPawn()->GetName() : TEXT("NONE"),
|
||||
NewPlayer && NewPlayer->PlayerInput ? TEXT("yes") : TEXT("no"));
|
||||
}
|
||||
@@ -18,4 +18,8 @@ class GIGLINE_API AGigLineGameModeBase : public AGameModeBase
|
||||
|
||||
public:
|
||||
AGigLineGameModeBase();
|
||||
|
||||
protected:
|
||||
/** Logs whether a local player connected to a pawn with input (QA). */
|
||||
virtual void HandleStartingNewPlayer_Implementation(class APlayerController* NewPlayer) override;
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "GigLineHUD.h"
|
||||
|
||||
#include "Engine/Canvas.h"
|
||||
#include "Engine/Engine.h"
|
||||
|
||||
void AGigLineHUD::DrawHUD()
|
||||
{
|
||||
Super::DrawHUD();
|
||||
|
||||
if (!Canvas || AttributionLine.IsEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Bottom-left, above any letterbox: the standard place for map/data credits.
|
||||
const float LineHeight = Canvas->ClipY > 0 ? 22.0f * TextScale : 22.0f * TextScale;
|
||||
const float X = MarginPx;
|
||||
const float Y = Canvas->ClipY - MarginPx - LineHeight * 2.1f;
|
||||
|
||||
const FLinearColor Shadow(0.0f, 0.0f, 0.0f, 0.55f);
|
||||
const FLinearColor Ink(1.0f, 1.0f, 1.0f, 0.92f);
|
||||
|
||||
// Shadow pass for legibility over bright pavement and sky.
|
||||
DrawText(AttributionLine, Shadow, X + 1.0f, Y + 1.0f, nullptr, TextScale);
|
||||
DrawText(LicenceLine, Shadow, X + 1.0f, Y + LineHeight + 1.0f, nullptr, TextScale * 0.82f);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/HUD.h"
|
||||
#include "GigLineHUD.generated.h"
|
||||
|
||||
/**
|
||||
* Always-on geographic attribution for the shipped demo.
|
||||
*
|
||||
* The demo is built from OpenStreetMap data, which is ODbL 1.0 licensed and
|
||||
* requires attribution wherever a derived database or produced work is shown.
|
||||
* This HUD draws the required credit in the bottom-left corner of the screen so
|
||||
* it is present in the game, not only in the repository documents.
|
||||
*
|
||||
* Plain AHUD::DrawText is used deliberately: no UMG widget asset is needed, so
|
||||
* the attribution cannot be accidentally dropped from a cook by a missing
|
||||
* widget reference.
|
||||
*/
|
||||
UCLASS()
|
||||
class GIGLINE_API AGigLineHUD : public AHUD
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void DrawHUD() override;
|
||||
|
||||
/** Main credit line. Must match the ODbL attribution requirement. */
|
||||
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");
|
||||
|
||||
/** Screen-space margin in pixels. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attribution")
|
||||
float MarginPx = 18.0f;
|
||||
|
||||
/** Text scale relative to the default canvas font. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attribution")
|
||||
float TextScale = 1.45f;
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "GigLineNPCPerson.h"
|
||||
|
||||
#include "Components/CapsuleComponent.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "Engine/SkeletalMesh.h"
|
||||
|
||||
AGigLineNPCPerson::AGigLineNPCPerson()
|
||||
{
|
||||
// The MPFB human is ~1.66 m tall, ~0.43 m wide. Half-height 83 cm leaves
|
||||
// the head top at ~1.66 m, so a 1.60 m camera does not clip it. Radius
|
||||
// ~35 cm accommodates the relaxed idle pose (arms at the sides).
|
||||
GetCapsuleComponent()->InitCapsuleSize(35.0f, 83.0f);
|
||||
GetCapsuleComponent()->SetRelativeLocation(FVector(0.0f, 0.0f, 83.0f));
|
||||
|
||||
// Visible body: the imported MPFB human on ACharacter's built-in mesh
|
||||
// component, feet at Z 0 (capsule base lowered to the floor).
|
||||
USkeletalMeshComponent* Mesh = GetMesh();
|
||||
Mesh->SetRelativeLocation(FVector(0.0f, 0.0f, -83.0f));
|
||||
Mesh->SetRelativeRotation(FRotator(0.0f, -90.0f, 0.0f));
|
||||
Mesh->SetCastShadow(true);
|
||||
|
||||
static ConstructorHelpers::FObjectFinder<USkeletalMesh> MeshAsset(
|
||||
TEXT("/Game/Test/Character/character"));
|
||||
if (MeshAsset.Succeeded())
|
||||
{
|
||||
Mesh->SetSkeletalMeshAsset(MeshAsset.Object);
|
||||
Mesh->SetSkeletalMesh(MeshAsset.Object);
|
||||
}
|
||||
|
||||
// An NPC pawn has no player input; hand it to AI when it is spawned into
|
||||
// the world (the 0.5.0 pedestrian navigation will drive it).
|
||||
AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;
|
||||
bUseControllerRotationYaw = false;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "GigLineNPCPerson.generated.h"
|
||||
|
||||
/**
|
||||
* Reusable NPC/pedestrian prefab built on the MPFB human asset.
|
||||
*
|
||||
* Deliberately NOT the 0.1.0 player (which is AGigLineCharacter, a capsule +
|
||||
* camera). This is the visible-body template for future NPCs/pedestrians
|
||||
* (0.5.0): ACharacter's collision capsule sized to the ~1.66 m MPFB human,
|
||||
* with the character skeletal mesh on the built-in mesh component as the
|
||||
* visible body, feet at Z 0.
|
||||
*
|
||||
* Head-height guarantee: capsule half-height 83 cm (total 1.66 m), so the head
|
||||
* top sits at ~1.66 m; a camera/view at 1.60 m sits just below eye level and
|
||||
* does not clip the head or neck. Set HeadHeightCm after observing the posed
|
||||
* mesh if the eye socket lands elsewhere.
|
||||
*/
|
||||
UCLASS()
|
||||
class GIGLINE_API AGigLineNPCPerson : public ACharacter
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AGigLineNPCPerson();
|
||||
|
||||
/** Observed head height (cm above Z 0), used to verify camera clearance. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "NPC")
|
||||
float HeadHeightCm = 155.f;
|
||||
};
|
||||
@@ -21,16 +21,28 @@ 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;
|
||||
if (SettleCountdown <= 0.f)
|
||||
{
|
||||
bWaitingForSettle = false;
|
||||
const FString FileName = FString::Printf(TEXT("review-%02d-%s"), CurrentShot,
|
||||
CurrentShot < ShotNames.Num() ? *ShotNames[CurrentShot] : *FString::FromInt(CurrentShot));
|
||||
// Use the stable custom name only (no list-index prefix): inserting or
|
||||
// reordering cameras must not renumber files and orphan the old ones.
|
||||
const FString Name = CurrentShot < ShotNames.Num()
|
||||
? ShotNames[CurrentShot]
|
||||
: FString::FromInt(CurrentShot);
|
||||
const FString FileName = FString::Printf(TEXT("review-%s"), *Name);
|
||||
// Resolution is the game window's; the runner passes -ResX/-ResY.
|
||||
FScreenshotRequest::RequestScreenshot(FileName, false, false);
|
||||
FScreenshotRequest::RequestScreenshot(FileName, /*bInShowUI=*/true, false);
|
||||
bWaitingForScreenshot = true;
|
||||
}
|
||||
return;
|
||||
|
||||
+1
-1
@@ -42,4 +42,4 @@ WGS84 (lat, lon) → EPSG:26911 (E, N metres) → project-local metres → Unrea
|
||||
|
||||
The extract, both El Capitan footprints, the demo bounds, the building sets, the street profile, cross-sections, and the 3DEP DEM window are recorded in `Geo/verification/` and `Geo/manifests/`. The local CRS and origin are fixed in `manifests/local-origin.md`.
|
||||
|
||||
Open items live in `ROADMAP.md` under **Geographic data**.
|
||||
Open items live in `TODO.md` under **Geographic data**.
|
||||
@@ -22,7 +22,7 @@ The window is the block plus a ~100 m margin on the east and ~70 m on the other
|
||||
The raster is reprojected by the service, not by us, so it was checked against the service's own point query (`getSamples`) and against the independent point service used in `../verification/2026-11-06-elevation.md`:
|
||||
|
||||
| Point | Raster pixel | Service `getSamples` | Earlier point probe |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| --- | --: | --: | --: |
|
||||
| Hollywood × Highland (origin) | 115.911 | 115.910 | 115.911 |
|
||||
| Hollywood × Orange Dr | 119.422 | 119.418 | 119.418 |
|
||||
| Mid-block crossing | 117.593 | — | 117.53 |
|
||||
|
||||
@@ -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.
|
||||
@@ -38,7 +38,7 @@ Reproduce it the same way, or with `pyproj` once a geo environment exists. If a
|
||||
Optional check values, all NAD83 / UTM 11N, and the same points as local X/Y relative to the origin. Use them to confirm a transformation before trusting a blockout.
|
||||
|
||||
| Point | WGS84 lat, lon | Easting | Northing | Local X | Local Y |
|
||||
| --- | --- | ---: | ---: | ---: | ---: |
|
||||
| --- | --- | --: | --: | --: | --: |
|
||||
| Hollywood × Orange Dr | 34.101543, −118.341749 | 376 235.784 | 3 774 227.529 | −279.605 | +3.446 |
|
||||
| **Origin: Hollywood × Highland** | 34.101545, −118.338718 | **376 515.389** | **3 774 224.083** | 0.000 | 0.000 |
|
||||
| Hero NW corner | 34.101412, −118.340299 | 376 369.380 | 3 774 211.202 | −146.009 | −12.881 |
|
||||
@@ -53,10 +53,10 @@ Note the sign pattern: the whole block lies at **negative X** and the hero at **
|
||||
|
||||
## Axis convention
|
||||
|
||||
| Axis | Meaning |
|
||||
| --- | --- |
|
||||
| Local X | easting − origin easting, metres, east positive |
|
||||
| Local Y | northing − origin northing, metres, north positive |
|
||||
| Axis | Meaning |
|
||||
| ------- | ------------------------------------------------------------ |
|
||||
| Local X | easting − origin easting, metres, east positive |
|
||||
| Local Y | northing − origin northing, metres, north positive |
|
||||
| Local Z | up, metres, elevation relative to the origin's ground height |
|
||||
|
||||
The block then spans roughly **X −279 m to 0 m**, Y a few tens of metres either side, which keeps float precision comfortable in Unreal.
|
||||
@@ -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).
|
||||
@@ -16,7 +16,7 @@ Those gutter positions bracket a carriageway of roughly **20–22 m**, which ind
|
||||
## Section table
|
||||
|
||||
| Along block | Lowest point | Road crown | Cross-fall across the road | Gutter candidates |
|
||||
| ---: | ---: | ---: | ---: | --- |
|
||||
| --: | --: | --: | --: | --- |
|
||||
| 0.0 m | 118.78 @ −10 | ~119.41 @ 0 | ~0.6 m / 2 % | −10, +10 |
|
||||
| 40.0 m | 118.43 @ −11 | ~118.94 @ 0 | ~0.5 m / 2 % | −11, +10.5 |
|
||||
| 80.0 m | 117.84 @ −10 | ~118.33 @ 0 | ~0.5 m / 2 % | −10, +10.5–11.5 |
|
||||
|
||||
@@ -14,13 +14,13 @@ Note: the point service answers in 6–8 s per request, so the probe runs reques
|
||||
|
||||
Five positions along the 279.4 m block, at the south sidewalk (12.6 m from the centreline), the road centreline, and the north sidewalk (13.6 m).
|
||||
|
||||
| Along block | South sidewalk | Centreline | North sidewalk |
|
||||
| ---: | ---: | ---: | ---: |
|
||||
| 0.0 m (Orange Dr) | 118.90 | 119.36 | 119.59 |
|
||||
| 69.8 m | 118.11 | 118.46 | 118.49 |
|
||||
| 139.7 m | 116.95 | 117.41 | 117.50 |
|
||||
| 209.5 m | 116.04 | 116.27 | 116.47 |
|
||||
| 279.4 m (Highland Ave) | 115.65 | 115.91 | 116.10 |
|
||||
| Along block | South sidewalk | Centreline | North sidewalk |
|
||||
| ---------------------: | -------------: | ---------: | -------------: |
|
||||
| 0.0 m (Orange Dr) | 118.90 | 119.36 | 119.59 |
|
||||
| 69.8 m | 118.11 | 118.46 | 118.49 |
|
||||
| 139.7 m | 116.95 | 117.41 | 117.50 |
|
||||
| 209.5 m | 116.04 | 116.27 | 116.47 |
|
||||
| 279.4 m (Highland Ave) | 115.65 | 115.91 | 116.10 |
|
||||
|
||||
Crossings: Orange Dr 118.96 m, mid-block 117.53 m, Highland 115.95 m.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ Crossing endpoints sit within about 1 m of the kerb line, so these lengths appro
|
||||
Mapped sidewalk centrelines sit **12.6 m** (south) and **13.6 m** (north) from the road centreline. Building setbacks, measured to each building's nearest vertex:
|
||||
|
||||
| Side | Building | Setback | Frontage extent |
|
||||
| --- | --- | ---: | ---: |
|
||||
| --- | --- | --: | --: |
|
||||
| S | 6922 | 14.6 m | 66.1 m |
|
||||
| S | 6902 | 14.5 m | 31.0 m |
|
||||
| S | **6840 El Capitan Entertainment Centre** | **14.5 m** | **25.7 m** |
|
||||
|
||||
@@ -2,10 +2,45 @@
|
||||
|
||||
Shared working memory between sessions and collaborators. Short, dated entries with evidence: the command, the log line, or the file path.
|
||||
|
||||
Scope: facts and traps that cost time to establish. Planned work lives in [ROADMAP.md](ROADMAP.md); formal setup, pins, and licensing live in [Docs/](Docs/).
|
||||
Scope: facts and traps that cost time to establish. Planned work lives in [TODO.md](TODO.md); development direction lives in [ROADMAP.md](ROADMAP.md); formal setup, pins, and licensing live in [Docs/](Docs/).
|
||||
|
||||
---
|
||||
|
||||
## 2026-11-08 — Documentation toolchain installed; asciidoc→markdown conversion
|
||||
|
||||
- **pandoc 3.13** installed at `~/.local/bin/pandoc` from the [GitHub release tarball](https://github.com/jgm/pandoc/releases/tag/3.13) (statically linked, 161 MB). The Fedora repos carry only `ghc-pandoc` (Haskell library), not the CLI.
|
||||
- **asciidoctor 2.0.26** layered via `rpm-ostree install rubygem-asciidoctor` — **pending reboot** to activate. As a workaround, a distrobox Fedora container (`distrobox enter arch-convert`) runs asciidoctor without rebooting.
|
||||
- **prettier 3.9.10** installed globally via npm (Node v24.20.0). Project config: `.prettierrc` (`proseWrap: never`, `tabWidth: 2`), `.prettierignore` (skips `Binaries/`, `*.uasset`, `.agents/`).
|
||||
- **Conversion chain:** `.adoc` → `asciidoctor -b docbook5` → `.xml` → `pandoc -f docbook -t gfm` → `.md` → `prettier --write`. Pandoc's built-in `asciidoc` reader cannot handle Asciidoctor tables or attributes — it must go through docbook5.
|
||||
- **Post-processing fixes:** glossary `cols="e,2e"` emphasis leaks italic into cell content (strip `*...*` wrapping), `®` from the R key binding becomes `(R)`, anchor IDs `{#...}` stripped, escaped quotes/brackets normalised.
|
||||
- **All `.adoc` files removed.** Markdown is the canonical format. Architecture doc lives in `Docs/Architecture/` as 13 section `.md` files + combined `architecture.md`.
|
||||
- All `.md` in the repo passes `prettier --check` (23 files formatted, `.agents/` excluded as vendored content).
|
||||
|
||||
## 2026-11-08 — QA branch merged; demo is walkable
|
||||
|
||||
- **PR #7** squash-merged to `main` as `52b1212` — terrain collision fix, spawn correction, `UGigLineAssetTools`, `HighResShot` fix, `-qaselftest` harness.
|
||||
- **QA self-test results (from merged main):** walk 750 cm / 2 s (Z steady 456), reset 0 cm from spawn, pause/unpause pass, transect 84 samples / 0 holes (ground Z 317..395).
|
||||
- **Traversal release check closed:** the swept transect measures walkability rather than assuming it.
|
||||
- `gameplay-crossing-0.1.0` branch deleted (already merged); `gig-line-judge` worktree removed.
|
||||
- `qa-input-collision-fix-0.1.0` branch still exists locally/remote (squash merge has no shared ancestry, `git branch -d` refuses; delete with `-D` after release tag).
|
||||
- `GigLine SIM` / `GigLine GROUND` TEMP diagnostic logging still in `GigLineCharacter.cpp` — must be removed before `v0.1.0` tag.
|
||||
|
||||
## 2026-11-08 — Reset was returning to mid-air; QA self-test switch
|
||||
|
||||
- **`ResetToStart()` restored a `StartLocation` captured in `BeginPlay`**, which is the PlayerStart position _above_ grade (Z 600) so the capsule never spawns embedded. Every reset therefore dropped the player 1.4 m back into the air. Fix: capture `StartLocation` on the first grounded frame (repeating 0.25 s timer, cleared once captured) — not a one-shot timer, which can fire after the player has already started walking and record a mid-stride position.
|
||||
- **`-qaselftest`** drives the checks headlessly and logs `GigLine QA: ... PASS/FAIL`. It runs on a **core ticker, not a world timer**: pausing the game stops world timers, so a timer-driven test freezes exactly when it proves pause works.
|
||||
- Current results: walk 746 cm / 2 s with Z steady, reset 0 cm from spawn, pause `IsGamePaused()` 1, unpause 0, transect 84 samples / 0 holes (ground Z 317..395).
|
||||
|
||||
## 2026-11-08 — The fall-through was an 8 m invisible box (QA branch)
|
||||
|
||||
- **Symptom:** player "spawned above ground but never fell", sat at Z 909, `fall=0`, `mode=1` (Walking), no fall-through, view looked plausible so it was mistaken for working.
|
||||
- **Cause:** `Terrain_DEM` had only a **bounding-box simple collision proxy** (Interchange default). A line trace down at _every_ Y through the block hit `Demo_Terrain_DEM` at exactly **819** — the mesh bbox max Z. The whole street was under an invisible lid; the pawn stood on the box, 8 m above the road.
|
||||
- **Proof:** a debug `LineTraceSingleByChannel` profile at Y −1912…+1447 reported `top=819 actor=Demo_Terrain_DEM` for the entire street. After the fix it reports `Sidewalk_North 395`, `Road_Hollywood 366`, `Sidewalk_South 317`.
|
||||
- **Fix:** force complex-as-simple collision on the terrain. Python **cannot** set it (`StaticMesh`/`BodySetup` expose no such property; `dir()` shows only `lod_for_collision`). Added `UGigLineAssetTools::SetComplexCollisionAsSimple` in the C++ module (`BodySetup->CollisionTraceFlag = CTF_UseComplexAsSimple`, UE 5.8 no longer has `bUseComplexAsSimpleCollision`).
|
||||
- **Second gotcha:** the flag lives on the **asset**, not the map. Setting it in a builder session and saving only the `.umap` loses it — `EditorAssetLibrary.save_loaded_asset()` is required, otherwise the next run reloads the bbox proxy.
|
||||
- **Third gotcha:** `HighResShot 1280 720` means **multiplier 1280** (UE warns "the high resolution screenshot multiplier is too large for your system"). The correct form is `HighResShot 1280x720`.
|
||||
- Resulting spawn: `(-13336, -1150, 600)` — inside the carved corridor, lands on the road at Z 456 (feet 368 on road top 366).
|
||||
|
||||
## 2026-11-07 — Publication audit
|
||||
|
||||
- MPFB 2.0.18's installed `blender_manifest.toml` identifies its code as GPL-3.0-or-later; its versioned `LICENSE.md` explicitly puts bundled mesh, texture, and rig assets under CC0 1.0. The earlier CC0 claim for the add-on code was wrong.
|
||||
@@ -18,10 +53,7 @@ Scope: facts and traps that cost time to establish. Planned work lives in [ROADM
|
||||
## 2026-11-07 — Unreal Engine 5.8.4
|
||||
|
||||
- **Extracted** to `/run/media/system/DATA/Engines/UE_5.8.4` (73 GiB, `unzip` exit 0). CL `58902052`, `++UE5+Release-5.8`.
|
||||
- **Gotcha: the editor needs a writable `~/.config/Epic`.** In the agent shell it crashed with signal 11 twice and looked like a broken install. The log named it:
|
||||
`create dir('/home/zwitschi/.config/Epic/') failed: errno=30 (Read-only file system)` →
|
||||
`Fatal error: Failed to create Intermediate directory '…/.config/Epic/UnrealEngine/5.8/Intermediate/'`.
|
||||
Rerunning with `HOME=/tmp/uehome XDG_CONFIG_HOME=/tmp/uehome/.config` reached `LogInit: Display: Engine is initialized.`
|
||||
- **Gotcha: the editor needs a writable `~/.config/Epic`.** In the agent shell it crashed with signal 11 twice and looked like a broken install. The log named it: `create dir('/home/zwitschi/.config/Epic/') failed: errno=30 (Read-only file system)` → `Fatal error: Failed to create Intermediate directory '…/.config/Epic/UnrealEngine/5.8/Intermediate/'`. Rerunning with `HOME=/tmp/uehome XDG_CONFIG_HOME=/tmp/uehome/.config` reached `LogInit: Display: Engine is initialized.`
|
||||
- **Vulkan RHI is still unproven.** The successful run used `-NullRHI`. Untested: `UnrealEditor -vulkan` from a desktop terminal.
|
||||
- A packaged game does not need `~/.config/Epic`, so this failure does not follow the build.
|
||||
|
||||
@@ -41,8 +73,7 @@ Scope: facts and traps that cost time to establish. Planned work lives in [ROADM
|
||||
|
||||
## 2026-11-06 — Blender and Blosm, headless
|
||||
|
||||
- **`flatpak run` does not work here.** Call the binary inside the app tree:
|
||||
`B=/var/lib/flatpak/app/org.blender.Blender/x86_64/stable/active/files`, then `LD_LIBRARY_PATH="$B/blender/lib" "$B/blender/blender" -b …` with `BLENDER_USER_SCRIPTS="$HOME/.var/app/org.blender.Blender/config/blender/5.2/scripts"`.
|
||||
- **`flatpak run` does not work here.** Call the binary inside the app tree: `B=/var/lib/flatpak/app/org.blender.Blender/x86_64/stable/active/files`, then `LD_LIBRARY_PATH="$B/blender/lib" "$B/blender/blender" -b …` with `BLENDER_USER_SCRIPTS="$HOME/.var/app/org.blender.Blender/config/blender/5.2/scripts"`.
|
||||
- **Blosm needs two headless workarounds**, both already in `Art/blender/blockout/blosm_blockout.py`:
|
||||
1. it refuses to import until an asset directory is set, and the check fires before the mode is honoured;
|
||||
2. in background mode the default level table is empty, and the add-on then takes a path with a broken relative import (`from gui import addDefaultLevels`). Seeding one entry avoids it.
|
||||
@@ -83,7 +114,7 @@ Scope: facts and traps that cost time to establish. Planned work lives in [ROADM
|
||||
|
||||
## 2026-11-07 — Screenshot pipeline
|
||||
|
||||
- **`-executePythonScript` + editor is unreliable for captures**: the editor quits right after a *successful* deferred script (async screenshot requests never render), and after `load_map` in a commandlet every `spawn_actor` call returns None — build scenes in the default world instead.
|
||||
- **`-executePythonScript` + editor is unreliable for captures**: the editor quits right after a _successful_ deferred script (async screenshot requests never render), and after `load_map` in a commandlet every `spawn_actor` call returns None — build scenes in the default world instead.
|
||||
- **Robust in-game capture recipe**: commandlet builds a map (`ue_build_review_map.py`), then the game runs in `-game` mode and a tiny C++ actor (`AGigLineShotActor`) walks camera setups via `SetViewTarget` + `FScreenshotRequest::RequestScreenshot`. In 5.8 `FScreenshotRequest` has no resolution argument (window size via `-ResX/-ResY`) and no `IsFinished()` — poll `!IsScreenshotRequested()`. Lives in `UnrealClient.h`, not `Misc/ScreenshotManager.h`.
|
||||
- **Commandlet-only quirks**: bulk render-target readback (`read_render_target_raw`, `export_render_target`) silently returns empty in commandlets; single-pixel `read_render_target_uv` works. Game targets must use `BuildSettingsVersion.V7`.
|
||||
|
||||
@@ -140,10 +171,177 @@ Scope: facts and traps that cost time to establish. Planned work lives in [ROADM
|
||||
- **Docs drift silently after a refactor.** Moving the geo scripts to `Geo/tools/` and the Blender builders into `Art/blender/*/` left stale paths in six documents and two tool READMEs. Grep for old paths (`.md` and `.py`) after any move, not just the ones that error at runtime.
|
||||
- **Every new tool needs a row in `Tools/README.md` and `Docs/build.md`**, or the next session will not know it exists. Both now list the full content pipeline in order.
|
||||
|
||||
|
||||
## 2026-11-07 — Character diffuse bake
|
||||
|
||||
- **`img.save()` writes black for an image created in memory** even though Cycles `bpy.ops.object.bake` does update `img.pixels` correctly (verified in-memory px were warm). Writing the PNG ourselves from `img.pixels` is required.
|
||||
- **A per-pixel `struct.pack` loop over `img.pixels` is catastrophic in headless Blender** (256² took 25 s). Blender ships numpy: read `img.pixels[:]` once into a float array, reshape `(h,w,4)`, clip→uint8, reverse rows for PNG top-down, then zlib-compress. Whole 1024² encode ≈ 0.3 s.
|
||||
- **MPFB's v2-skin node tree does not resolve to a simple base colour**, so a Cycles DIFFUSE bake returns black. Since the FBX export material replaces the node tree anyway, synthesize the diffuse directly (`make_diffuse`): warm skin body + a darker shorts-suggestive bottom band.
|
||||
- **A read-only `.umap` silently makes `save_map` return `False` with no error** (already recorded); re-hit in the review pipeline — `chmod u+w` the maps before rebuilding, otherwise the new character material never appears in shots.
|
||||
|
||||
## 2026-11-07 — screenshot judge tool
|
||||
|
||||
- `Tools/judge_screenshot.py` — runs an OpenRouter vision model over a screenshot vs an expected-scene description. Reads `OPENROUTER_API_KEY` from the repo `.env` (ignored). Base64 data-URI image under `image_url.url`; OpenAI-compatible body → `POST /api/v1/chat/completions`. `openai/gpt-4o-mini` returns a `{verdict, confidence, reason, missing}` JSON. It flags genuinely missing content (e.g. `main`'s pre-warm-skin character shot judged "no"); it is a pre-check, not the human verdict.
|
||||
- Test one: `Tools/judge_screenshot.py --image Screenshots/review-01-character.png --expect "<scene>" --verbose`. Bulk: `--shots` (parses the README rows).
|
||||
|
||||
## 2026-11-07 — Blender skill packs: discovery depth
|
||||
|
||||
- **The 94 packs were never registered with the `skill` tool**, only readable as files. The loader resolves `<skills-root>/<pack>/SKILL.md` one level down; `.agents/skills/blender-skills/` is a _container_ of 94 packs, so it was skipped silently (no warning, no error). Symptom: `skill("archviz")` unresolvable even though `archviz/SKILL.md` exists.
|
||||
- **Fix applied:** 94 symlinks in `~/.agents/skills/<pack>` → `<repo>/.agents/skills/blender-skills/<pack>/`. The catalog then lists all of them (verified by calling `skill` on `collision-proxy`: base dir resolves through the link). `references/` stays unlinked — it is a shared folder with no `SKILL.md`, not a pack.
|
||||
- **The AGENTS.md "MCP-driven" caveat is overstated.** 41 of 94 SKILL.md mention MCP, mostly only in the `description` line; the bodies are method guides (naming, proportions, validation checklists) that work headless. Do not skip a pack because it advertises MCP.
|
||||
- **Skill names in AGENTS.md/TODO.md all resolve** except `references`, which is the folder above, not a pack.
|
||||
- Symlinks are user-level: a fresh machine needs the same 94 links before the packs are loadable by name.
|
||||
- **A shared checkout can commit your uncommitted edit for you, or lose it.** This entry was written but not yet committed when another session ran a `git commit` that included `MEMORY.md`; it landed inside their commit (`54b6239`) with a message that does not mention it. Had they overwritten the file instead, the work would have been gone with no reflog to recover from. On a shared worktree, either commit a shared file immediately or check `git log -S` for it before assuming the edit is missing.
|
||||
|
||||
## 2026-11-07 — Street visibility and the LFS lockable trap
|
||||
|
||||
- **Every street surface (road, sidewalks, kerbs, markings) had 100 % downward face normals** — `strip()`'s natural winding (rows along +s, cols along +normal) faces the ground. Invisible in engine for as long as the terrain covered them; the corridor cut exposed the black trench. Fixed by reversing the winding in `strip()`, `kerb_faces()` and `crossing_band()`.
|
||||
- **`*.blend` and `*.umap` are LFS-lockable in `.gitattributes`** — Git LFS checks them out read-only (444). `bpy.ops.wm.save_as_mainfile` then fails ("path is not writable") and `EditorLoadingAndSavingUtils.save_map` silently returns False. All Blender builders now chmod u+w before saving and treat a failed save as fatal; the UE map builders chmod the .umap first. A failed save used to leave downstream steps building from stale files — that produced a degenerate-mesh FBX (all-same vertex indices from an index-arithmetic bug) that cost an hour to trace.
|
||||
- **FBX SDK refuses to triangulate the crown-twisted crossing quads** ("unable to triangulate mesh ... will be omitted") — emit triangles from the builder, and make sure per-stripe vertex indices advance (`len(verts)` must be captured before the loop, not per-corner).
|
||||
- Demo-map coordinate note: UE Y is south-positive; the facade at Y −1450 and the player start at Y −3500 are on opposite sides of the road (Y −1382..+1047) — arrangement is correct; the Sidewalk_North/South mesh names are swapped relative to their physical sides (documented in export_site_fbx.py).
|
||||
|
||||
## 2026-11-07 — ReviewMap corruption: deprecated level calls + save-order bug
|
||||
|
||||
- **`EditorLevelLibrary.load_level`/`new_level` are deprecated no-ops in `-nullrhi` commandlets** (they return without switching worlds, so staged actors land in whatever world is current and get saved over the wrong map). Use the `LevelEditorSubsystem`: `unreal.get_editor_subsystem(unreal.LevelEditorSubsystem).load_level(path)` actually switches. Symptom was ReviewMap containing Demo actors + `ShotSequencer`.
|
||||
- **Map-save ordering bug in `Tools/ue_build_review_map.py`**: the script saved ReviewMap _after_ the DemoMap section, but the switch back to ReviewMap fails silently because the map only exists in memory at that point — so the final `save_map` wrote the demo world over ReviewMap. Fix: save ReviewMap first, then stage the demo cameras, and never switch back. Both maps verified by probe (ReviewMap lists Review actors; DemoMap lists Demo actors).
|
||||
- Rebuilt ReviewMap after the fixes: character, both cubes (A grounded, B floating at 2 m), pawn, NPC prefab, hero facade all present; user re-approved the shots. README cube naming corrected (README had A/B swapped vs the script).
|
||||
|
||||
## 2026-11-07 — Materials and lighting in the demo map
|
||||
|
||||
- **Blender flat diffuse colours do not survive FBX -> Unreal.** Every site mesh (road, sidewalks, kerbs, crossing paint) imported with a material asset that had a default _white_ base colour, and the terrain imported with `WorldGridMaterial` (UE's bright grey grid). That — not the sun — is what made the demo look blown out and washed white. Fix: create the colours in UE (`M_Asphalt`, `M_Concrete`, `M_Ground`, `M_RoadPaint` via `Constant3Vector`) and assign them per actor in `Tools/ue_build_demo_map.py`.
|
||||
- **Auto-exposure makes review shots non-comparable**: the same road rendered washed-out in one framing and near-black in another because each camera saw a different amount of sky. Fix: an unbound `PostProcessVolume` with `AEM_HISTOGRAM` and `auto_exposure_min_brightness == auto_exposure_max_brightness` (fixed exposure). Do not use `AEM_MANUAL` + `auto_exposure_bias` — a bias of 11 blew the whole frame out.
|
||||
- **`DirectionalLightComponent.light_color` takes a `Color` struct, not `LinearColor`** (`unreal.Color(r=255,g=245,b=230,a=255)`); passing `LinearColor` raises a nativize error.
|
||||
- **One band per object loses geometry downstream.** `crossing_band()` created `Crossing_Markings` each call, so Blender renamed the second and third to `.001`/`.002`; `site_assembly.py` appends by exact name, so only one crossing ever reached the game. Build all bands into a single mesh instead of one object per band.
|
||||
- **Street arc length -> UE X:** s=0 is the west end of the block (UE X −27966 cm) and s increases eastward, so the designated mid-block crossing at s=130.1 m is UE X ≈ −14956, not the −24291 used by an earlier camera (that is the s=36.6 crossing).
|
||||
- **Camera placement in the demo map needs the mesh bounds, not intuition.** The hero façade body spans UE Y −1457..−200, so a "street level" camera at Y −400 is _inside_ the building and renders its unlit back face. Read `get_bounding_box()` on the staged meshes before choosing a camera.
|
||||
- **"LIGHTING NEEDS TO BE REBUILT" (Build Lighting Only / `BuildLighting`) appears whenever a map has Stationary/Static lights and no baked lightmap.** For a fully dynamic scene the clean fix is `r.AllowStaticLighting=False` in `Config/DefaultEngine.ini` plus `set_mobility(ComponentMobility.MOVABLE)` on the directional light, sky light and height fog (and MOVABLE on the staged mesh components). Nothing to build, warning gone. Note the cvar is read at startup, so each new commandlet/game run picks it up.
|
||||
- The Android File Server `SecurityToken` kept reappearing in `Config/DefaultEngine.ini`; the plugin is disabled and excluded from shipping, so the value is cleared and must stay empty — it is a credential and must not be committed.
|
||||
|
||||
## 2026-11-07 — Worktrees and the material look pass
|
||||
|
||||
- **A fresh git worktree has no compiled game binaries.** `Binaries/` is gitignored, so `UnrealEditor-Cmd` on a new worktree exits "abnormally (error code: 1)" before running the Python script, with no Python error in the log. Run `Engine/Build/BatchFiles/Linux/Build.sh GigLineEditor Linux Development -project=<worktree>/Game/GigLine/GigLine.uproject` first.
|
||||
- `/tmp/gigline/*.txt` result files are **shared across worktrees**. Reading `/tmp/gigline/demo-map.txt` after a run can show another worktree's stale output — check the file mtime, or use a per-worktree result path.
|
||||
- **Flat Constant3Vector materials read as plastic.** Giving each surface a roughness plus a world-space-noise blend between two shades (WorldPosition → Multiply → Noise → Lerp between two Constant3Vectors → BaseColor) is enough to stop the blockout looking like a toy; no textures required.
|
||||
|
||||
## 2026-11-07 — The Y-side bug (found when the frontage was added)
|
||||
|
||||
- **UE convention in this project: south = +Y, north = −Y.** Ground truth is the street mesh: `Sidewalk_North` is Y −1912..−1053 and `Sidewalk_South` Y +718..+1447.
|
||||
- The hero facade had been placed at **Y −1450 (north side)** and X −12024..−9420 — 26 m east of the 6840 footprint and on the wrong side of the street. It only looked right because it stood alone in an empty field; the approved screenshots were of a building with no context.
|
||||
- The two north-side buildings (TCL, Tussaud's) were similarly on the **south** side: `export_north_buildings.py` mirrored Y, which put them where the frontage belongs.
|
||||
- Fix: hero at X −13336 / Y +1307 (the frontage gap between 6902 and 6838); frontage and north buildings exported **without** a Y mirror (the FBX preset already maps Blender −Y south to UE +Y south).
|
||||
- Lesson: **place a building only against a neighbour or the street mesh, never in isolation.** A single object in an empty map hides any number of placement errors; adding the adjacent frontage immediately exposed this one.
|
||||
|
||||
## 2026-11-07 — walkable demo QA bugs (fix branch qa-input-collision-fix-0.1.0)
|
||||
|
||||
- **"Stuck" window = wrong default map.** A plain `./GigLine.sh` opened `/Engine/Maps/Templates/OpenWorld` (empty engine template) instead of the demo, so nothing was interactive. `GameDefaultMap` was in `DefaultGame.ini` and not applied; it must live in `DefaultEngine.ini[/Script/EngineSettings.GameMapsSettings]` with `GlobalDefaultGameMode`. Verified: plain launch now loads `/Game/Hollywood/DemoMap`. Evidence: `LogLoad: LoadMap: /Engine/Maps/Templates/OpenWorld`.
|
||||
- **Input assets were pruned from the pak.** The Enhanced Input assets under `/Game/Input` were only soft-referenced at runtime, so the cooker excluded them (cook `ReferencedSet.txt` had zero `/Game/Input` lines -> all inputs nil at runtime). Fix: hard-reference via `ConstructorHelpers::FObjectFinder` in the character ctor (cooker follows hard refs) + `+DirectoriesToAlwaysCook`. Packaged log then showed `ctx=ok move=ok ... jump=ok`.
|
||||
- **Character fell through** the site: site meshes staged MOVABLE + PlayerStart far past the north sidewalk. Fix: STATIC mobility + explicit collision (QUERY_AND_PHYSICS/WorldStatic/BlockAll) and PlayerStart at Y -1200 on the sidewalk. Packaged run: `grounded=1 movement=Walking`.
|
||||
- Diagnose packaged runtime via the engine log (`.../Linux/GigLine/Saved/Logs/GigLine.log`); a headless `-nullrhi` commandlet cannot validate physics traces (even a spawned cube returns NO HIT there).
|
||||
|
||||
## 2026-11-08 — reference photography gaps closed (local-only)
|
||||
|
||||
- Closed the two missing reference angles for the hero façade, both from Wikimedia
|
||||
Commons (gitignored deliverables under `Art/reference/`, provenance in sidecars):
|
||||
- `el-capitan-entrance/` — `File:Hollywood El Capitan entrance.jpg` (+ detail),
|
||||
Andreas Praefcke, CC BY 3.0. This is the isolated hero (6840) entrance/door
|
||||
recess that the notes doc had claimed Commons didn't hold. Query that found it:
|
||||
`--query "El Capitan entrance"`.
|
||||
- `el-capitan-night/` — `File:El Capitan theater with Tron marquee.jpg`
|
||||
(CC BY-SA 3.0) + West Side Story premiere (Adam Jones, CC BY 2.0). The theatre is
|
||||
the building NEXT to 6840; treat as proximity evidence only, not the hero.
|
||||
- Commons throttles hard: HTTP 429 after a batch. Fetch fewer files with
|
||||
`--delay`/separate calls and wait ~15-20s between runs (`queue.py` tool default
|
||||
1 req/s is insufficient once rate limited).
|
||||
- Task was claimed on branch `ref-photo-0.1.2`; delivers only the TODO closure (all
|
||||
actual reference files are gitignored, so no code branch content). Reported to
|
||||
coordinator for merge/cleanup.
|
||||
|
||||
## 2026-11-08 — security-game scan: concepts copied, licensing ignored (owner)
|
||||
|
||||
- Scanned `/run/media/system/DATA/Documents/06-games/security-game` for reference
|
||||
material vs our `Art/reference/`. Result: its **photo set** (`docs/screens/refs/
|
||||
hollywood/`, 9 files) is already fully duplicated in our `Art/reference/hollywood/`
|
||||
(with SIDECAR/BOARD copies) — nothing new there.
|
||||
- What we lacked: its **AI concept renders** (`docs/screens/concepts/
|
||||
hollywood_boulevard/`, 12 PNGs + `.refgen.json`). Copied into
|
||||
`Art/reference/el-capitan-concepts/` with a folder README. **Owner explicitly
|
||||
said ignore licensing completely** — they are gitignored local development
|
||||
reference, never committed or shipped; `.refgen.json` still records the model
|
||||
and source photo per file for traceability.
|
||||
- Use concepts for mood/lighting intent only; geometry truth stays with the photo
|
||||
refs (facade, entrance, night marquee, TCL/Tussauds, Masonic 1922, etc.).
|
||||
- The security-game `realism/` A/B screenshots and `concepts/joke_shop/` are its
|
||||
own engine/venue renders — not relevant to our 6840 modeling.
|
||||
|
||||
## 2026-11-08 — ODbL review closed; remaining active TODO items finished
|
||||
|
||||
- Wrote `Docs/odbl-review.md` (decision 2026-11-08): the shipped block geometry
|
||||
(streets/footprints/sidewalks) is a Produced Work of the OpenStreetMap database
|
||||
(ODbL 1.0). Obligations held: on-screen attribution via AGigLineHUD ("©
|
||||
OpenStreetMap contributors" + "Data © OSM, ODbL 1.0 — openstreetmap.org/copyright"),
|
||||
ODbL link, and the raw extract committed in Geo/ with a manifest (date, bbox,
|
||||
SHA-256). Share-alike is limited to the data layer; project code/art remain CC0
|
||||
(Docs/licensing.md decision). 3DEP DEM is US Government public domain, unrelated
|
||||
to ODbL.
|
||||
- Closed both [A] items in TODO: (1) sidewalks/roads/collision — collision
|
||||
verified; remaining kerb radii/aprons/kerb-height explicitly tracked as 0.1.1
|
||||
patch tasks (Sidewalk outer edge, Crossing ramp gradient, Kerb height);
|
||||
(2) ODbL review — recorded. The [D] Blosm-import (blockout cleaning) item stays
|
||||
deferred (tracked separately).
|
||||
- Branch: odbl-review-0.1.0 (docs + TODO closure only).
|
||||
|
||||
## 2026-11-08 — placement release check; north-buildings camera was 9.5 km off
|
||||
|
||||
- Release check 2 (placement vs references): rendered demo, probed DemoMap world
|
||||
positions, compared to Geo references. Matches: road 24.3 m (ref crossings
|
||||
20.9-25.9), sidewalk centreline offsets 12.5/13.2 m (ref 12.6/13.6), hero
|
||||
frontage 26.0 m / setback ~13 m (ref 25.75/14.5), six south-frontage buildings
|
||||
+ TCL/Tussaud's in verified OSM positions.
|
||||
- **Bug found:** the `demo-north-buildings` camera sat at X -22800 (the buildings'
|
||||
own X), aiming at its own position -> 85%-sky screenshot. Probed real geometry:
|
||||
TCL bbox X -22889..-18442, Y -9431..-1802; Tussaud's X -27071..-22799,
|
||||
Y -7586..-1888; hero at X -13336, Y +1307 (frontage 26 m). Camera now at
|
||||
(X -22750, Y +1600) looking north; sky fraction 85% -> 41%.
|
||||
- Getting actor world bounds: spawn at loc(0,0,0) means static mesh bbox IS the
|
||||
world footprint (FBX carries the geo offset). Probe by loading the level
|
||||
explicitly (load_level) — a fresh commandlet starts on Untitled otherwise.
|
||||
|
||||
## 2026-11-08 — collision/seam audit done; QA self-test extended
|
||||
|
||||
- `-qaselftest` transect widened from 84 samples (3 lane centres) to **1456
|
||||
samples / 16 rows** including kerb/road/sidewalk seam Ys (probed: N side
|
||||
-1912..-1053, kerb -1382..-1053, road -1382..+1047, kerb +718..+1047, S side
|
||||
+718..+1447). 0 holes.
|
||||
- **New wall probe:** ground-referenced horizontal 30 m trace at +160 cm. First
|
||||
attempt used a fixed Z 160 and started INSIDE the earth (street is at Z ~366)
|
||||
-> every row reported a dist-0 blocker. Always ground the probe first.
|
||||
- **Guard:** GigLineShotActor must skip rounds under `-qaselftest`, else
|
||||
bQuitWhenDone closes the game before the transect phase (observed: game exited
|
||||
at t=2s with "ShotActor: done (4 shots)" + RequestExit).
|
||||
- Results on packaged build: walk 761cm/2s, reset 0cm, pause/unpause, transect
|
||||
0 holes, wall 0/35 -> all PASS. Evidence recorded in Docs/release-checks.md.
|
||||
|
||||
## 2026-11-08 — setup logs gated behind a cvar (diagnostics item)
|
||||
|
||||
- The remaining spawn/input setup logs (settle-timer "start captured", "GigLine
|
||||
IN:" pushed/bound/errors) are now gated behind `gigline.SetupLogs`
|
||||
(TAutoConsoleVariable, default **0** via `DefaultEngine.ini [ConsoleVariables]`).
|
||||
Re-enable at runtime: `gigline.SetupLogs 1`.
|
||||
- The `-qaselftest` QA logs stay ungated (deliberate release-check feature per
|
||||
Docs/release-checks.md). Verified packaged build: 0 setup-log lines, QA suite
|
||||
still all PASS (walk/reset/pause/transect 0 holes/wall 0-35).
|
||||
- Branch qa-diagnostics-0.1.0. SIM/GROUND probes were removed earlier in the
|
||||
DefaultKeyMappings fix (PR #8).
|
||||
|
||||
## 2026-11-08 — v0.1.0 released
|
||||
|
||||
- All six `0.1.0` release checks pass and are recorded in `Docs/release-checks.md`:
|
||||
traversal (seam transect 0 holes), placement vs references, collision+packaging
|
||||
(wall probe 0/35), 60 fps at 1440p (2.42 ms avg; TSR on, VSync off, no FG),
|
||||
fresh-checkout build, packaged Linux build with controls shown in-game.
|
||||
- `CHANGELOG.md` gets the `0.1.0` entry; the completed `0.1.0` section was removed
|
||||
from `TODO.md` (goal stays in `ROADMAP.md`); tag `v0.1.0` cut on `main`.
|
||||
- Owner approved the regenerated screenshot set (PR #15) on 2026-11-08.
|
||||
- THIRD_PARTY_NOTICES switched to checkbox statuses (`[x]`/`[P]`/`[ ]`); all tools
|
||||
incl. Blosm, QGIS and Git LFS are now verified as tool-only.
|
||||
- Next work: the `0.1.1` collision-detail patch (sidewalk outer edge 15.8 cm step,
|
||||
crossing ramp gradient 10 %, kerb height assumption, traversal regression).
|
||||
@@ -55,9 +55,7 @@ Progress lives in `ROADMAP.md`; session findings live in `MEMORY.md`; rendered r
|
||||
- `Tools/` — repeatable import/export helpers.
|
||||
- `Docs/` — setup, licenses, architecture, release checks.
|
||||
|
||||
Track `.uasset`, `.umap`, `.blend`, and large source binaries with LFS.
|
||||
Ignore generated `Binaries/`, `Intermediate/`, `Saved/`, and Derived Data Cache.
|
||||
Do not commit Unreal Engine source, credentials, or non-redistributable assets.
|
||||
Track `.uasset`, `.umap`, `.blend`, and large source binaries with LFS. Ignore generated `Binaries/`, `Intermediate/`, `Saved/`, and Derived Data Cache. Do not commit Unreal Engine source, credentials, or non-redistributable assets.
|
||||
|
||||
## Geographic and Art Pipeline
|
||||
|
||||
@@ -80,7 +78,7 @@ Use a consistent scale test, named export presets, and repeatable import setting
|
||||
- Walkable sidewalks and a designated street crossing.
|
||||
- First-person movement, mouse look, collision, reset, and pause.
|
||||
- Recognizable hero façades; simplified background buildings.
|
||||
- Fixed daytime lighting, basic ambience, and geographic attribution.
|
||||
- Fixed daytime lighting and basic ambience.
|
||||
- Packaged Linux build with documented controls.
|
||||
|
||||
### Excluded
|
||||
@@ -101,7 +99,7 @@ Driving, interiors, crowds, traffic simulation, multiplayer, and a full-city map
|
||||
Solo-development estimate: **approximately 30–40 weeks**, subject to asset quality and available hours.
|
||||
|
||||
| Release | Weeks | Larger task | Small patch tasks after release |
|
||||
| --- | ---: | --- | --- |
|
||||
| --- | --: | --- | --- |
|
||||
| `0.1.0` | 1–6 | Local setup and initial walkable Hollywood block | `0.1.1` collision fixes; `0.1.2` scale/façade corrections; `0.1.3` packaging and setup fixes |
|
||||
| `0.2.0` | 7–9 | MPFB player body, third-person camera, basic animation | `0.2.1` rig fixes; `0.2.2` skin/hair tuning; `0.2.3` camera clipping fixes |
|
||||
| `0.3.0` | 10–13 | One Chaos vehicle, enter/exit interaction, closed driving route | `0.3.1` handling tuning; `0.3.2` vehicle collision fixes; `0.3.3` input fixes |
|
||||
@@ -122,7 +120,5 @@ Finish each milestone's release checks before expanding scope. Cut map size and
|
||||
- Separate code, art, engine, and geographic-data licensing.
|
||||
- Record asset provenance and redistribution permissions in `THIRD_PARTY_NOTICES.md`.
|
||||
- Verify licenses for every MPFB asset, texture, animation, vehicle, and audio file.
|
||||
- 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.
|
||||
@@ -6,7 +6,7 @@ A Linux-first Unreal Engine game about walking and driving through a real city.
|
||||
|
||||
`0.1.0` is in progress. The workstation, engine, geo pipeline and project bootstrap are done and verified; a walkable `DemoMap` exists with terrain, street, sidewalks, kerbs and the hero façade placed from measured coordinates. Façades are still blockouts, and materials are flat colours — both are planned patches.
|
||||
|
||||
Current state is in [ROADMAP.md](ROADMAP.md); what was learned the hard way is in [MEMORY.md](MEMORY.md).
|
||||
Current tasks are in [TODO.md](TODO.md) and the development plan is in [ROADMAP.md](ROADMAP.md); what was learned the hard way is in [MEMORY.md](MEMORY.md).
|
||||
|
||||
## Layout
|
||||
|
||||
@@ -31,7 +31,8 @@ The engine is installed outside the repository and pinned in [Docs/toolchain.md]
|
||||
|
||||
## Documents
|
||||
|
||||
- [ROADMAP.md](ROADMAP.md) — living task list, source of truth for progress
|
||||
- [TODO.md](TODO.md) — living task list, source of truth for progress
|
||||
- [ROADMAP.md](ROADMAP.md) — development plan: milestone goals, scope, and release gates
|
||||
- [MEMORY.md](MEMORY.md) — shared session memory: discoveries, traps, decisions
|
||||
- [AGENTS.md](AGENTS.md) — contributor and agent rules
|
||||
- [PLAN.md](PLAN.md) — scope, stack, release policy, timeline
|
||||
@@ -43,4 +44,4 @@ The engine is installed outside the repository and pinned in [Docs/toolchain.md]
|
||||
|
||||
## Licensing
|
||||
|
||||
Code licence is an open decision (MIT proposed). Engine, art, and geographic data are licensed separately; see [Docs/licensing.md](Docs/licensing.md). OSM-derived output carries "© OpenStreetMap contributors".
|
||||
Code licence is an open decision (MIT proposed). Engine, art, and geographic data are licensed separately; see [Docs/licensing.md](Docs/licensing.md).
|
||||
+49
-361
@@ -1,10 +1,10 @@
|
||||
# ROADMAP.md
|
||||
|
||||
Living roadmap for this project. This file is the single source of truth for current progress.
|
||||
Development roadmap for this project: milestone goals, scope, decisions, and release gates. Actionable tasks, claims, and progress marks live in [TODO.md](TODO.md).
|
||||
|
||||
## Status at a glance — 2026-11-07 09:45
|
||||
## Status at a glance — 2026-11-08 15:30
|
||||
|
||||
Workstation, geo pipeline, and engine validation are done. The art pipeline has a first blockout, terrain, and street in metres, in one shared coordinate frame. **Project bootstrap is complete**: `Game/GigLine` builds, packages, and both content-acceptance tests (static-mesh scale, MPFB skeletal character) pass. Remaining `0.1.0` work is geo detailing, asset production, gameplay, and release checks.
|
||||
Workstation, geo pipeline, and engine validation are done. The art pipeline has a first blockout, terrain, and street in metres, in one shared coordinate frame. **Project bootstrap is complete**: `Game/GigLine` builds, packages, and both content-acceptance tests (static-mesh scale, MPFB skeletal character) pass. **`v0.1.0` released 2026-11-08**: the walkable demo, hero façade, verified block frontage, first-person controls, ODbL attribution, and every release check pass (traversal, placement, collision, 60 fps at 1440p, fresh-checkout build, packaged build with controls). Next: the `0.1.1`–`0.1.4` patch series (collision detail, scale/façade corrections, packaging fixes, real building façades), then `0.2.0` (player body, third-person camera, animation).
|
||||
|
||||
| Area | State |
|
||||
| --- | --- |
|
||||
@@ -16,482 +16,170 @@ Workstation, geo pipeline, and engine validation are done. The art pipeline has
|
||||
| Reference | 72 images local, uncommitted; hero entrance located; inherited eight-step claim overturned |
|
||||
| Unreal project | bootstrap complete: C++ project builds, blank Linux build packaged + launch-tested, scale test and MPFB skeletal character accepted |
|
||||
|
||||
**Next up:** model the hero façade and block frontage from the reference set, then first-person traversal on the site terrain. **Release gate:** `v0.1.0` is tagged only on the owner's explicit go-ahead once the release checks pass — not automatically when the checkboxes close.
|
||||
|
||||
**Open decision:** whether shipped builds carry a derived OSM database (ODbL posture).
|
||||
**Next up:** model the hero façade and block frontage from the reference set, then first-person traversal on the site terrain (tasks in [TODO.md](TODO.md)). **Release gate:** `v0.1.0` is tagged only on the owner's explicit go-ahead once the release checks pass — not automatically when the TODO items close.
|
||||
|
||||
## 0.1.0 — Local setup and walkable Hollywood block (weeks 1–6)
|
||||
|
||||
### Environment setup
|
||||
**Goal:** a walkable exterior demo around 6840 Hollywood Blvd / El Capitan Entertainment Centre, built on verified geography, in one shared coordinate frame, and validated on the Linux workstation. Setup details are in [Docs/setup.md](Docs/setup.md) and pins in [Docs/toolchain.md](Docs/toolchain.md).
|
||||
|
||||
Status detail in [Docs/setup.md](Docs/setup.md); pins in [Docs/toolchain.md](Docs/toolchain.md). The workstation audit is local only.
|
||||
**Environment and bootstrap (complete):** the Bazzite workstation, Unreal 5.8.4 (headless and Vulkan GUI launch), the system clang toolchain, and the pinned Blender, MPFB, Blosm, and QGIS versions are in place. The C++ project builds, a blank Linux build packages and launches, and two content-acceptance tests pass: the static-mesh scale test (1 m = 100 cm, axis mapping east → +X, north → −Y, up → +Z) and the MPFB skeletal character import.
|
||||
|
||||
- [x] Update Bazzite using its supported mechanism; verify NVIDIA driver and Vulkan operation.
|
||||
- [x] Register an Epic account and accept the Unreal license; obtain a pinned Linux engine build or build from Epic's authorized source repository.
|
||||
- [x] Validate Unreal runs on Bazzite before asset production (not an officially supported distribution). 2026-11-07: headless `-NullRHI` smoke test **and** full editor GUI launch with the Vulkan RHI passed — device `NVIDIA GeForce RTX 5070 Ti`, Vulkan loader 1.4.350, `VULKAN_SM5`, map check 0 errors. Only benign log noise (Android Turnkey status, telemetry DNS failures).
|
||||
- [x] Install the engine-required compiler/SDK, Git, Git LFS, and an editor; use Distrobox for isolated CLI dependencies where useful. clang 23.1.4 / cmake 4.3.0 / ninja 1.13.2 installed; this engine build bundles no Linux clang, so the system compiler is the toolchain. **Accepted 2026-11-07:** the first C++ game-module compile and link (`libUnrealEditor-GigLine.so`) succeeded with system clang.
|
||||
- [x] Pin Blender, MPFB, Blosm, and QGIS versions; confirm add-on compatibility; record in `docs/toolchain.md`. Done: Blender 5.2.0 LTS, MPFB 2.0.18, Blosm 2.7.28, QGIS 4.2.3 with PROJ 9.9.0 (stale `lts` removed 2026-11-07), Unreal 5.8.4 finalized as pin after the editor GUI launch.
|
||||
- [x] Create repository structure (`Game/`, `Art/`, `Geo/`, `Tools/`, `Docs/`) with LFS tracking and ignore rules applied.
|
||||
- [x] Keep project and Derived Data Cache on SSD storage; reserve several hundred GB for engine, assets, caches, builds.
|
||||
**Geographic data (complete, with blockout detail open):** the OSM extract, address, footprints, demo bounds, EPSG:26911 frame with a recorded origin, and the 3DEP 1 m DEM are verified and committed. The meter→centimetre conversion lives in one function, and every exporter must call it. Block buildings still need height data for three placeholders and cleaning or detailing of the Blosm blockout.
|
||||
|
||||
### Project bootstrap
|
||||
**Review and character (decision):** the review scene is the staging used for all visual approval. Its scope is the character's ground contact, the camera list, and the façade detail shots, and nothing in asset production or gameplay continues past it until these shots pass. The shot list is in `Screenshots/README.md`. Coordinator decision 2026-11-07: **`0.1.0` ships first-person only**, a capsule with a camera and no visible player body. The MPFB character asset (idle pose, warm skin) is the template for later **NPCs** (`0.2.0` body/camera toggle, `0.5.0` pedestrians). It is now a concrete reusable prefab, `AGigLineNPCPerson` (collision capsule + visible body, feet on Z 0), kept standalone and not wired into the `AGigLineCharacter` pawn.
|
||||
|
||||
- [x] Create a C++ Unreal project with Blueprint gameplay iteration and Enhanced Input. Created 2026-11-07 at `Game/GigLine/GigLine.uproject` (primary game module + default game mode, Enhanced Input plugin + input classes in `Config/DefaultInput.ini`); `GigLineEditor` target compiles clean with system clang (UBT: Result Succeeded).
|
||||
- [x] Package and launch a blank Linux build. 2026-11-07: UAT `BuildCookRun` (cook + build + stage + pak, Development config) succeeded in 56 s; 1.1 GiB archive at `/run/media/system/DATA/Builds/GigLine` (outside the repo, uncommitted). `GigLine.sh` launched on the desktop session, created a Vulkan swapchain, and rendered 759 frames across a 60 s test. Known minor: one handled `PSOPrecache` ensure fires at exit only; not blocking, watch it in `0.1.x`.
|
||||
- [x] Export one static mesh from Blender and import into Unreal; verify scale and materials. 2026-11-07: calibration scene (three 1 m cubes at origin / +10 m east / +10 m north) exported from Blender with preset `ue_metric_v1` (`Tools/scale_test_export.py`) and imported headless via `Tools/ue_scale_test_import.py`. Measured in UE: cubes exactly 100.00 cm, origin preserved, material imported. Axis mapping: **east → +X, north → −Y, up → +Z** (the Y mirror is inherent to the right-handed→left-handed conversion; importer handles winding).
|
||||
- [x] Export one MPFB character with baked materials and deform skeleton; verify import. 2026-11-07: MPFB 2.0.18 generated human (19 158 verts, 1.66 m tall) + standard rig with weights (137 bones) + v2 skin baked to a 1024² diffuse texture (`Tools/mpfb_character_export.py`, export preset `ue_character_v1`). Imported headless (`Tools/ue_character_import.py`) into `/Game/Test/Character`: `SkeletalMesh` + 138-bone `Skeleton` + `Material` with the baked `Texture2D` assigned to slot 0. Quirks recorded: 5.8 Interchange skips materials for skeletal FBX imports (finished in script); Blender 5.2 dropped the FBX `armature_nla` option; the baked image needs `image.filepath` set for FBX to reference it.
|
||||
- [x] Document initial setup in `Docs/`: engine install, project checkout, fresh-checkout build procedure. 2026-11-07: `Docs/setup.md` reconciled with reality (all engine/bootstrap items marked done with evidence); `Docs/build.md` completed with the fresh-checkout build, packaging, and headless-content-check procedures plus known engine quirks.
|
||||
**Asset production:** the scale test and the `ue_metric_v1` export and import presets are established. The hero façade first pass is built and screenshot-approved; the north-side buildings (TCL Chinese Theatre, Madame Tussaud's) are staged. Open: reference collection for the door recess and the night frame, the surrounding block frontage, sidewalks, roads and collision, and materials for all assets.
|
||||
|
||||
### Geographic data
|
||||
**Gameplay:** first-person movement, mouse look, pause, and reset are implemented. The walkable demo map is built but has not been checked by hand. Open: the designated crossing, daytime lighting and ambience.
|
||||
|
||||
Tools ported 2026-11-07: every geographic script now lives in `Geo/tools/` with `Geo/README.md` listing its purpose, and the Blender-side builders moved next to their outputs in `Art/blender/*/`, importing the shared helpers and the single unit conversion from `Geo/tools/osm_common.py`.
|
||||
**Publication:** history is rewritten, secrets are scanned, and LFS support is validated.
|
||||
|
||||
| Tool | Purpose |
|
||||
| --- | --- |
|
||||
| `osm_common.py` | parse `.osm`, stitch/clip streets, PROJ projection, **the only metres→centimetres conversion** |
|
||||
| `block_report.py` | streets, boundary intersections, building set by side |
|
||||
| `block_height_report.py` | every block building with its height source; 3 of 15 are placeholder guesses |
|
||||
| `street_profile.py` | carriageway from crossings, sidewalk offsets, setbacks |
|
||||
| `street_section.py` | DEM cross-sections, crown and gutters |
|
||||
| `elevation_probe.py` | 3DEP relief sampling |
|
||||
| `dem_stats.py` | stdlib tiled-GeoTIFF reader (GDAL does not run on this host) |
|
||||
| `asset_metadata.py` | writes and audits the `<asset>.geo.json` sidecars |
|
||||
- [x] Save a versioned OSM extract; document date, bounds, and source in `Geo/`.
|
||||
- [x] Acquire separately licensed elevation data if needed. Needed and acquired: USGS 3DEP 1 m DEM in EPSG:26911, public domain, NAVD88, 984 KB committed (`Geo/manifests/2026-11-06-3dep-dem.md`); measured relief 3.94 m.
|
||||
- [x] Download the 3DEP 1 m DEM for the demo bounds, reproject it to EPSG:26911 with the recorded origin and vertical datum, and record the source, clip bounds, and checksum in `Geo/`.
|
||||
- [x] In QGIS, set a suitable local projected CRS (e.g. EPSG:26911) and record a local origin. EPSG:26911, origin and projected easting/northing are recorded in `Geo/manifests/local-origin.md` with PROJ 9.9.0 control points; QGIS 4.2.3 is installed for the DEM reprojection step.
|
||||
- [x] Verify the address, building footprints, and demo boundaries for 6840 Hollywood Blvd against source data.
|
||||
- [~] Import OSM blockouts through Blosm; clean and detail them in Blender. Imported 2026-11-06 and re-framed to EPSG:26911. Tooling for the cleaning pass is now in `Geo/tools/` (height provenance, block report) and `Art/blender/blockout/blosm_blockout.py`. **Still open:** 3 of 15 block buildings carry no height data and render at a placeholder 6.4 m, roofs and facades are untextured, and Blosm `setupScript` façade styles are unused. Earlier: (`Art/blender/blockout/blockout-2026-11-06.blend`, 86 meshes, scale 1:1 against OSM, re-framed into EPSG:26911 with a 0.0 cm affine residual); cleaning and detailing not started.
|
||||
- [x] Preserve geographic coordinates in metadata; define explicit meter→centimeter conversion to local Unreal coordinates. Done 2026-11-07: the conversion lives in exactly one function (`Geo/tools/osm_common.py::to_unreal_local`) and every exporter must call it; `<asset>.geo.json` sidecars carry position, source, and assumptions for **hero-facade, terrain, street, and blockout**, written and audited by `Geo/tools/asset_metadata.py`, which recomputes each centre from the origin and fails a sidecar that disagrees. Props follow the same schema when they are imported. The frame is defined (`Geo/manifests/local-origin.md`: EPSG:26911, origin Hollywood × Highland, X east / Y north / Z up) and the ×100 conversion is now **verified** by the 2026-11-07 scale test, with the axis mapping recorded (north realizes as UE −Y). Left: build the production export preset for the actual site meshes using these settings.
|
||||
|
||||
### Review scene and player placement
|
||||
|
||||
Owner: this session. Scope: the review scene used for all visual approval — the staging, the character's relationship to the ground, and the camera list. Nothing in `Asset production` or `Gameplay` continues until these pass.
|
||||
|
||||
- [x] Put the character on the ground rather than floating. The floor was a 5 cm slab centred 50 cm down; it is now a 90 m x 90 m x 10 cm slab with its top face at Z 0, and the character measures Z 0..83 cm — feet on the plane (`Screenshots/README.md`).
|
||||
- [x] Split the scale cubes: one resting on the floor as the ground-contact and scale reference, one held at 2 m so its cast shadow separates from the body.
|
||||
- [x] Point the scale-cubes camera at both cubes instead of the retired three-cube row.
|
||||
- [x] **Character placement shot**: `review-03-player-placement` shows the pawn (`AGigLineCharacter`) staged by `Tools/ue_build_review_map.py` with a cube behind it. Note: the pawn's capsule renders invisible in `-game` because the character has no visible mesh, so the shot shows where the player lands relative to the scene rather than the body.
|
||||
- [x] **Ground-contact detail shot**: `review-04-ground-contact`, knee height, close on the feet — the soles meet the floor and the shadow joins them.
|
||||
- [x] **Shadow-comparison detail shot**: `review-05-shadow-compare`, both cubes in one frame from a low angle; the floating cube's shadow is detached, the grounded one's touches.
|
||||
- [x] **Detail shot of the hero façade portals**: `review-06-hero-portals` from 8 m at eye height now fills the frame with all three doorways. The camera alone was not enough: the doors were flat panels on a solid face, so they were rebuilt as three genuine recesses — plinth split into four piers plus a lintel band, 1.2 m reveal, stone jambs and lintels standing 0.10 m proud, three mullions each. Facade bounds are now exactly Z 0..14.50 m; a jamb overhang had been sinking the model 0.15 m into the ground.
|
||||
- [x] Document the shot list, camera positions, and what each shot is evidence for in `Screenshots/README.md`; nine shots, each with an approval line.
|
||||
- [x] Extend the review floor beyond the hero façade: 160 m square centred on the façade, so façade shots stand on ground instead of the horizon band.
|
||||
- [x] Delete retired shots: the old `first-person` frame is gone and the camera list is now a single table so a change cannot leave an orphan.
|
||||
|
||||
### Character development — decision: 0.1.0 is first-person only; NPCs planned later
|
||||
|
||||
Coordinator decision 2026-11-07: **`0.1.0` ships first-person only** — a capsule with a camera, no visible player body. It is **not** a player-avatar blocker for `0.1.0`.
|
||||
|
||||
The MPFB character asset (idle pose + warm skin, done below) is planned as the template for **NPCs** in later milestones (`0.2.0` adds the body/camera toggle; `0.5.0` adds pedestrians). Keep it as a standalone character/prefab rather than wiring it into the `AGigLineCharacter` pawn. Everything here is future/NPC work, not `0.1.0` scope.
|
||||
|
||||
- [x] Give the MPFB character asset a standing idle pose so review and NPC shots show a person, not an A-pose bind. Done: `Tools/mpfb_character_export.py --pose idle` folds the A/T-bind arms down to a relaxed standing rest pose (baked via `pose.armature_apply` before export; `upperarm01.L` rest z −0.71 → −1.91 rad), re-exported, re-imported (138 bones), rendered in `review-01-character`.
|
||||
- [x] Bake a second skin tone or clothing layer, so the character does not read as a grey mannequin in shots. Done: the v2-skin bake came out **black** and slow at 1024², so we synthesize a warm skin-tone diffuse directly (`Tools/mpfb_character_export.py` `make_diffuse`, with a darker shorts-suggestive bottom band) via a stdlib/numpy PNG writer. Character reads warm, not grey. Screenshot review pending (`Screenshots/review-01-character` re-pended).
|
||||
- [ ] (open) Keep the character as a reusable NPC prefab: a visible body, a collision capsule, and a head-height check that the camera does not clip at 1.60 m — for when NPCs/pedestrians are wired in (`0.2.0+`), not the `0.1.0` player.
|
||||
|
||||
### Asset production
|
||||
|
||||
- [x] Establish a consistent scale test, named export presets, and repeatable import settings. Scale test = the three-cube calibration scene (`Art/blender/scaletest/`, regenerable via `Tools/scale_test_export.py`); named export preset `ue_metric_v1` (FBX settings in that script); named import preset `ue_metric_v1` (`FbxImportUI` settings in `Tools/ue_scale_test_import.py`). Both scripts are the templates for production asset export/import.
|
||||
- [-] Collect reference photography for manual façade work. Local only, never committed: `Art/reference/` is gitignored. **72 images in eleven folders**: hero façades to 4k, the El Capitan Theatre exterior series, premiere-night staging, Ovation Hollywood, the north side, and the Lee Drug neighbour. Gaps largely closed by the Flickr/web-search pass: the hero entrance is now documented (three portals with individual short flights — the inherited "eight steps, 12 m wide" is overturned), and the 6806–6822 retail run appears in an oblique 2013 frame. Still missing: a clean isolated frame of the door recess and a night frame of 6840 itself.
|
||||
- [x] Download reference photography for the two north-side buildings inside the 50 m buffer — TCL Chinese Theatre (OSM way 424105908, 6925 Hollywood Blvd) and Madame Tussaud's (way 424105889, 6933 Hollywood Blvd). Done 2026-11-06 with `Tools/commons_fetch.py` against the Wikimedia Commons API: eleven frames in the local `Art/reference/north-chinese/` and `Art/reference/north-tussauds/`, provenance in each `sidecar.json`, nothing committed.
|
||||
- [x] Model hero façade for the El Capitan Entertainment Centre site. **Second pass built 2026-11-07** (proportions recalibrated from the 2009 photograph: entablature 3.4 m, parapet 2.2 m, architrave and cornice bands, square pedestals — the first pass had 5.7 m of blank wall above the capitals and read as a warehouse). **First pass built 2026-11-07** (`Tools/hero_facade_build.py` → `Art/blender/hero/hero-facade-2026-11-07.blend` + JSON part report with evidence per part → `Tools/ue_hero_facade_import.py` → `/Game/Hollywood/ElCapitan/SM_HeroFacade`, 1892 tris, 4 materials, placed on the measured building line). Six columns at 4.96 m over the 25.75 m frontage, plinth, three portal flights (5 risers, not the old 8), entablature 2.2 m, parapet 1.6 m with motto band and medallions. **Screenshot review pending** (`Screenshots/review-04`, `review-05`). Next pass: portal recesses read from the street, banner and window placement, then materials.
|
||||
- [ ] Model surrounding block frontage.
|
||||
- [x] Model simplified background buildings directly across Hollywood Boulevard. Done 2026-11-07, the two in-scope north-side buildings — TCL Chinese Theatre (SM_TCL_Chinese_Theatre) and Madame Tussaud's (SM_Madame_Tussauds) — taken from the verified OSM blockout volumes and exported with `ue_metric_v1`, with a Y-mirror so they land north of Hollywood Blvd across the street from the hero (TCL spans Unreal Y 1802..9431, Tussaud's 1888..7586; road centreline ≈ 0, hero façade at Y −1450). Staged in the DemoMap as `North_TCL_Chinese` / `North_Madame_Tussauds`; demo shot `review-03-demo-north-buildings` captured. Simple flat facade materials (surfaces are the `0.1.4` patch).
|
||||
- [-] Build sidewalks, roads, and collision meshes. Terrain, road, sidewalks and kerbs exist (`Art/blender/terrain/`, `street/`) and are assembled in `site/site-2026-11-06.blend`; collision and details (crossing markings, kerb radii, aprons) are not built. Ground must follow the DEM: 3.94 m fall along the block, ~2 % crown between the gutters at ±10–11 m from the centreline, north sidewalk higher than south. Kerb height is an unresolved assumption — the DEM does not resolve it (`Geo/verification/2026-11-06-cross-sections.md`).
|
||||
- [ ] Materials and look development for all exported assets.
|
||||
|
||||
### Gameplay - active - gig-line-gameplay
|
||||
|
||||
- [x] First-person movement, mouse look, and Enhanced Input bindings. Implemented 2026-11-07: `AGigLineCharacter` (C++, 1.75 m capsule, 1.60 m eye camera, 4 m/s walk, mouse look in deg/s, jump) as the default pawn, with five actions and an 11-entry context in `/Game/Input` built by `Tools/ue_create_input_assets.py`. **Not yet confirmed interactively** — launch PIE on `Test/ReviewMap` and walk. Screenshot review pending (`Screenshots/README.md`).
|
||||
- [~] Collision, reset, and pause functionality. Reset (**R**) and pause (**Escape**, a real game pause that releases the cursor) are implemented in `AGigLineCharacter` with `/Game/Input/IA_Reset` and `IA_Pause`. Collision now exists — terrain, road and sidewalks import as complex-as-simple, so a simple hull cannot ramp over the kerbs. **Not verified by hand**: walk, reset and pause need a human at the keyboard. No pause menu widget yet, only the pause itself.
|
||||
- [-] Build the walkable demo level: terrain, street, and hero facade assembled into one map with a start point on the north sidewalk, so the demo is something a player can walk rather than a review staging. **Built 2026-11-07** by `Tools/ue_build_demo_map.py` → `/Game/Hollywood/DemoMap`: sun/sky, `Hero_Facade`, six site meshes imported with collision (`Terrain_DEM`, `Road_Hollywood`, both sidewalks and both kerbs), and a `Demo_PlayerStart` on the north sidewalk 20 m back from the frontage. Demo views in `Screenshots/demo/` are ☐ pending review.
|
||||
- [ ] Walkable sidewalks and a designated street crossing. Crossing chosen: mid-block signalised crossing, OSM way 385648319, at 130 m along the block (`Geo/verification/2026-11-06-street-profile.md`).
|
||||
- [ ] Fixed daytime lighting and basic ambience.
|
||||
- [ ] Geographic attribution display ("© OpenStreetMap contributors" + license link).
|
||||
|
||||
### Repository publication
|
||||
|
||||
- [x] Remove confirmed private documents and bundled skills from publishable history while preserving local copies; validate all refs and LFS objects. Rewritten 2026-11-07; original history is in a separate private local backup.
|
||||
- [x] Scan all reachable Git history and the publication changes with Gitleaks 8.28.0. Removed an Android File Server security token from history and disabled the unused server; the rescans found no leaks.
|
||||
- [x] Review Unreal asset provenance via the generation/import scripts and inspect all 18 reachable Unreal asset versions for recognizable private-reference paths. No such paths were found; this is not a full binary-content audit.
|
||||
- [x] Review OSM source distribution notices: original data retains attribution and licence metadata, and the repository links directly to ODbL 1.0.
|
||||
- [ ] Review ODbL obligations for each shipped derivative database or produced work before public release.
|
||||
- [x] Validate Gitea LFS support. The first push succeeded on 2026-11-07; a separate clone fetched all 26 historical LFS objects and passed integrity checks.
|
||||
- [x] Commit and review the publication preparation changes before pushing. Initial publication commit: `101db3f`.
|
||||
|
||||
### Release checks
|
||||
|
||||
- [ ] Player can traverse both sides of the demo without falling through geometry. Blender pre-check passes: 0 raycast holes along both sidewalks and the designated crossing; worst seam step 15.8 cm at the sidewalk's outer edge (`Art/blender/README.md`). Engine traversal still to be tested.
|
||||
- [ ] Building placement, street scale, and site identity checked against references.
|
||||
- [ ] No blocking collision or packaging defects.
|
||||
- [ ] 60 FPS at 1440p on the dev workstation (Bazzite, Ryzen 7 7700X, 64 GB RAM, RTX 5070 Ti 16 GB) using a documented quality preset; record upscaling state; report performance without frame generation.
|
||||
- [ ] Fresh project checkout builds with the documented external engine installation.
|
||||
- [ ] Package Linux build with documented controls.
|
||||
- [ ] Tag `v0.1.0` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Release checks:** traversal on both sides of the demo, building placement and street scale checked against references, no blocking collision or packaging defects, 60 FPS at 1440p on the documented preset, a fresh checkout that builds with the documented engine installation, a packaged Linux build with documented controls, and then the `v0.1.0` tag and changelog entry. The detailed checks are tracked in [TODO.md](TODO.md).
|
||||
|
||||
## 0.1.1 — Collision fixes (patch)
|
||||
|
||||
Planned now, applied after `0.1.0` ships. Collision is not authored yet, so these are the defects already visible in the Blender pre-check; the engine pass will add to this list rather than replace it.
|
||||
|
||||
Suggested blender skills: **collision-proxy** (UCX/UHX naming, convex hulls, simple collider meshes — the core of the authoring pass), **qa-review** (validation checklists, viewport screenshots, ship/no-ship verdicts for the audit), **asset-optimization** (pre-export validation of every collider). Note: these skills assume MCP-driven Blender; use them as method guides for the headless scripts.
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] **Sidewalk outer edge against terrain.** The pre-check measured a **15.8 cm** step where a walk sample crosses from `Terrain_DEM` onto `Sidewalk_South` (`Art/blender/README.md`, s≈207 m). Mostly hidden by buildings; visible wherever the frontage is missing. Fix by extending the sidewalk mesh to meet the terrain, or by lowering the outer lip.
|
||||
- [ ] **Crossing ramp gradient.** The kerb-flush transitions measure **10–13 cm over 1.5 m ≈ 10 %**, steeper than the 8 % a real ramp uses (`Geo/tools/street_section.py` output, s≈33, 39, 126 m). Grade the three ramp openings properly.
|
||||
- [ ] **Collision authoring pass.** None exists yet: on Unreal import, complex-as-simple for terrain and street (walkable surfaces must not be approximated into ramps over kerbs), simple convex hulls per building. Record the settings in `Docs/build.md`.
|
||||
- [ ] **Kerb height is an assumption.** 0.15 m modelled, 0.13 m as built, and the 3DEP DEM cannot resolve a kerb at all. Check against the photographs and record the final figure.
|
||||
- [ ] Audit and fix any collision gaps or fall-through geometry found in `0.1.0` in the engine.
|
||||
- [ ] Regression-test traversal on both sides of the demo.
|
||||
- [ ] Verify no new packaging defects.
|
||||
- [ ] Tag `v0.1.1` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix the collision and walkability defects already visible in the Blender pre-check, and add to the list as the engine pass finds more. Scope: the sidewalk outer edge against terrain (15.8 cm step), the crossing ramp gradient (about 10 % against the 8 % a real ramp uses), the collision authoring pass (complex-as-simple for terrain and street, simple hulls per building), the kerb height assumption, and traversal regression tests. Collision authoring uses the `collision-proxy`, `qa-review`, and `asset-optimization` skills as method guides.
|
||||
|
||||
## 0.1.2 — Scale and façade corrections (patch)
|
||||
|
||||
Suggested blender skills: **references** (reference-image-match and visual-match-checklist templates — measure the guessed heights and colonnade proportions against the photographs), **archviz** (real-world scale and façade composition discipline), **camera-cinematography** (reproduce the reference camera angles when comparing renders to photos).
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Correct building scale and street proportions flagged against references.
|
||||
- [ ] Refine hero façade details where reference photography shows discrepancies.
|
||||
- [ ] **Replace the placeholder heights.** Three of the fifteen block buildings carry no OSM height and render at a guessed 6.4 m (`Geo/tools/block_height_report.py`). Measure them off the photographs and correct the blockout.
|
||||
- [ ] Resolve the remaining unknown on the hero: the colonnade currently occupies about 45 % of the height against roughly 50–60 % in the photographs. Fix the vertical proportions.
|
||||
- [ ] Tag `v0.1.2` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** correct building and street proportions against the reference photography. Scope: the three placeholder heights (guessed at 6.4 m), the hero façade detail, and the colonnade proportion (about 45 % of the height against roughly 50–60 % in the photographs). Use the `references`, `archviz`, and `camera-cinematography` skills.
|
||||
|
||||
## 0.1.3 — Packaging and setup fixes (patch)
|
||||
|
||||
Suggested blender skills: **export-pipeline** and **unreal-export** (scale/pivot/normals/materials/collision verification) if any packaging defect traces back to the Blender export presets.
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Fix any packaging or build issues encountered in `0.1.x`.
|
||||
- [ ] Update `Docs/` setup instructions for anything that changed.
|
||||
- [ ] Tag `v0.1.3` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix any packaging or build issues found in `0.1.x` and bring `Docs/` up to date. Patches contain fixes only. If a Blender export preset is at fault, check it with the `export-pipeline` and `unreal-export` skills.
|
||||
|
||||
## 0.1.4 — Proper building façades (patch)
|
||||
|
||||
Planned explicitly so the shipped blockouts are not mistaken for finished architecture. **The existing geometry is deliberately kept** — this patch replaces surfaces and detail, not footprints or massing.
|
||||
|
||||
Suggested blender skills: **procedural-modeling** + **geometry-nodes** (the façade module kit — bay, pilaster, cornice, parapet, window, shopfront, door — as reusable node groups), **archviz** (real-world scale and façade composition), **materials** (procedural brick, concrete, stone, render, glass — the ported generators), **texture-workflow** (AO/curvature/normal bakes and decals for the motto band and medallions), **uv-workflow** (texel density and modular UV layouts for the kit), **scene-assembly** (assembling kit pieces onto the fifteen block buildings), **lookdev** (screenshot-comparison loop until shading reads correctly), **historical-worlds** or **realistic-style** (1920s Hollywood Blvd neoclassical direction), **asset-optimization** + **unreal-export** (pre-export validation, `ue_metric_v1` preset), **qa-review** (final gate before the `Tools/render_screenshots.sh` review).
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] **Define the façade module.** One set of reusable pieces (bay, pilaster, cornice, parapet, window, shopfront, door) at a fixed module, exported with the `ue_metric_v1` preset so every building assembles from the same kit.
|
||||
- [ ] **Replace placeholder materials.** Every blockout building currently carries Blender flat colours. Port the procedural generators from `Tools/sibling_asset_audit.py`'s sibling project (`brick_facade`, `seamless_concrete`, `asphalt`) and add stone, render, and glass, then wire them through Unreal material instances.
|
||||
- [ ] **Give OSM buildings a façade style.** Blosm exposes `scene.blosm.setupScript` (`setup/neoclassical.py` is the example) so a building can carry a real elevation instead of a plain extrusion. Build one style script for the south frontage and one for the north.
|
||||
- [ ] **Hero façade detail pass.** Ionic capitals and fluting, the parapet motto band and medallions as geometry or decal, window rhythm behind the colonnade, and the three portal flights at their photographed proportions.
|
||||
- [ ] **El Capitan Theatre (6838) and the retail neighbours.** Model from the 18-frame 5472×3648 reference set; the theatre shares the measured 4.96 m bay rhythm with the temple.
|
||||
- [ ] Review every façade change with `Tools/render_screenshots.sh` before tagging.
|
||||
- [ ] Tag `v0.1.4` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** replace the flat placeholder surfaces on the blockout buildings with real façades, keeping the existing footprints and massing. Scope: a reusable façade module kit (bay, pilaster, cornice, parapet, window, shopfront, door) exported with `ue_metric_v1`; procedural materials (brick, concrete, stone, render, glass) wired through Unreal material instances; Blosm façade style scripts for the south and north frontages; the hero façade detail pass; and El Capitan Theatre (6838) and the retail neighbours from the reference set. Use the façade, materials, UV, and QA skills named in `TODO.md`.
|
||||
|
||||
## 0.2.0 — MPFB player body, third-person camera, basic animation (weeks 7–9)
|
||||
|
||||
### Character setup
|
||||
|
||||
- [x] Generate MPFB human base mesh; export baked materials and deform skeleton. Done in bootstrap: idle-pose 19 158-vert human with a 137-bone standard rig, `Tools/mpfb_character_export.py` (now `--pose idle` + warm `make_diffuse`).
|
||||
- [x] Import character into Unreal; validate skeleton, scale, and materials. Done: `/Game/Test/Character` — `SkeletalMesh` (138 bones), `Skeleton`, warm `M_Character_Export` material with baked `T_Character_Diffuse`.
|
||||
- [ ] Promote the character asset into a reusable NPC prefab (visible body, collision capsule, idle pose) ready for pedestrians (`0.5.0`) once the third-person body lands.
|
||||
- [ ] Create basic idle/walk animation set.
|
||||
- [ ] Verify animation plays correctly on the imported rig.
|
||||
|
||||
### Camera and controls
|
||||
|
||||
- [ ] Add third-person camera following the player body.
|
||||
- [ ] Add first/third-person toggle.
|
||||
- [ ] Add camera collision to prevent clipping through geometry.
|
||||
- [ ] Document any input changes.
|
||||
|
||||
### Release checks
|
||||
|
||||
- [ ] Third-person traversal works end-to-end in the demo area.
|
||||
- [ ] No blocking animation or rig defects.
|
||||
- [ ] Performance target still met at 1440p.
|
||||
- [ ] Tag `v0.2.0` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** a visible, animated player body with a third-person camera and a first/third-person toggle, walking end-to-end in the demo area. The MPFB character is promoted to a reusable NPC prefab for `0.5.0`.
|
||||
|
||||
## 0.2.1 — Rig fixes (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Fix skeletal/rigging defects reported after `0.2.0`.
|
||||
- [ ] Tag `v0.2.1` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix skeletal and rigging defects reported after `0.2.0`.
|
||||
|
||||
## 0.2.2 — Skin and hair tuning (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Tune skin shading and hair materials on the player character.
|
||||
- [ ] Tag `v0.2.2` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** tune skin shading and hair materials on the player character.
|
||||
|
||||
## 0.2.3 — Camera clipping fixes (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Fix remaining camera clipping cases in `0.2.x`.
|
||||
- [ ] Tag `v0.2.3` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix the remaining camera clipping cases in `0.2.x`.
|
||||
|
||||
## 0.3.0 — Chaos vehicle, enter/exit, closed driving route (weeks 10–13)
|
||||
|
||||
### Vehicle production
|
||||
|
||||
- [ ] Model one vehicle in Blender with collision meshes; export to Unreal.
|
||||
- [ ] Set up Chaos Vehicle with wheel and physics configuration.
|
||||
- [ ] Implement enter/exit interaction.
|
||||
- [ ] Build a closed driving route with boundaries.
|
||||
- [ ] Tune basic handling to a drivable state.
|
||||
|
||||
### Release checks
|
||||
|
||||
- [ ] Player can enter, drive the closed route, and exit without defects.
|
||||
- [ ] No blocking vehicle collision or packaging defects.
|
||||
- [ ] Performance target still met at 1440p.
|
||||
- [ ] Tag `v0.3.0` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** one drivable vehicle with Chaos Vehicle physics, enter and exit interaction, and a closed driving route with boundaries.
|
||||
|
||||
## 0.3.1 — Handling tuning (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Refine vehicle handling feel based on `0.3.0` feedback.
|
||||
- [ ] Tag `v0.3.1` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** refine vehicle handling feel based on `0.3.0` feedback.
|
||||
|
||||
## 0.3.2 — Vehicle collision fixes (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Fix vehicle collision defects (mesh/physics mismatches).
|
||||
- [ ] Tag `v0.3.2` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix vehicle collision defects (mesh and physics mismatches).
|
||||
|
||||
## 0.3.3 — Input fixes (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Fix vehicle/character input issues in `0.3.x`.
|
||||
- [ ] Tag `v0.3.3` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix vehicle and character input issues in `0.3.x`.
|
||||
|
||||
## 0.4.0 — Small connected neighborhood and map streaming (weeks 14–17)
|
||||
|
||||
### World expansion
|
||||
|
||||
- [ ] Extend geo pipeline to a small connected neighborhood beyond the demo block.
|
||||
- [ ] Produce modular buildings, sidewalks, and roads for the expanded area.
|
||||
- [ ] Set up World Partition with streaming cells.
|
||||
- [ ] Configure HLOD for distant buildings.
|
||||
- [ ] Verify no seams or popping between streamed cells during traversal.
|
||||
|
||||
### Release checks
|
||||
|
||||
- [ ] Continuous walking (and driving) across the expanded area works.
|
||||
- [ ] Streaming does not introduce blocking defects.
|
||||
- [ ] Performance target re-validated at 1440p with streaming active.
|
||||
- [ ] Tag `v0.4.0` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** extend the geo pipeline to a small connected neighborhood with World Partition streaming and HLOD, with no visible seams while walking or driving across it.
|
||||
|
||||
## 0.4.1 — Seam fixes (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Fix visible seams between streamed cells.
|
||||
- [ ] Tag `v0.4.1` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix visible seams between streamed cells.
|
||||
|
||||
## 0.4.2 — Streaming corrections (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Correct streaming/loading issues found in `0.4.x`.
|
||||
- [ ] Tag `v0.4.2` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** correct streaming and loading issues found in `0.4.x`.
|
||||
|
||||
## 0.4.3 — HLOD tuning (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Tune HLOD quality and distance settings.
|
||||
- [ ] Tag `v0.4.3` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** tune HLOD quality and distance settings.
|
||||
|
||||
## 0.5.0 — Pedestrians, crosswalk behavior, ambient traffic (weeks 18–21)
|
||||
|
||||
### Simulation
|
||||
|
||||
- [ ] Add pedestrian characters with navigation, reusing the idle-pose character asset as the NPC prefab (`/Game/Test/Character`).
|
||||
- [ ] Implement crosswalk behavior (pedestrians use the designated crossing).
|
||||
- [ ] Add limited ambient traffic on the street network.
|
||||
- [ ] Optimize simulation cost to stay within frame budget.
|
||||
|
||||
### Release checks
|
||||
|
||||
- [ ] Pedestrians and traffic behave without blocking defects.
|
||||
- [ ] Performance target re-validated at 1440p.
|
||||
- [ ] Tag `v0.5.0` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** pedestrians that navigate and use the designated crossing, limited ambient traffic on the street network, and simulation cost held within the frame budget. Pedestrians reuse the idle-pose character prefab.
|
||||
|
||||
## 0.5.1 — Navigation fixes (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Fix pedestrian navigation issues.
|
||||
- [ ] Tag `v0.5.1` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix pedestrian navigation issues.
|
||||
|
||||
## 0.5.2 — Crossing fixes (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Fix crosswalk behavior defects.
|
||||
- [ ] Tag `v0.5.2` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix crosswalk behaviour defects.
|
||||
|
||||
## 0.5.3 — Simulation optimization (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Optimize pedestrian/traffic simulation cost.
|
||||
- [ ] Tag `v0.5.3` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** optimize pedestrian and traffic simulation cost.
|
||||
|
||||
## 0.6.0 — Visual/audio pass and evening lighting preset (weeks 22–24)
|
||||
|
||||
### Look and sound
|
||||
|
||||
- [ ] Create an evening lighting preset.
|
||||
- [ ] Complete a visual polish pass across the neighborhood.
|
||||
- [ ] Add ambient audio (city ambience, traffic, basic SFX).
|
||||
- [ ] Verify licenses for all added audio files.
|
||||
|
||||
### Release checks
|
||||
|
||||
- [ ] Both day and evening presets render without defects.
|
||||
- [ ] Audio plays without gaps or licensing issues.
|
||||
- [ ] Performance target re-validated at 1440p.
|
||||
- [ ] Tag `v0.6.0` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** an evening lighting preset alongside the day preset, a neighbourhood-wide visual polish pass, and ambient audio with verified licences.
|
||||
|
||||
## 0.6.1 — Lighting fixes (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Fix lighting defects in day/evening presets.
|
||||
- [ ] Tag `v0.6.1` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix lighting defects in the day and evening presets.
|
||||
|
||||
## 0.6.2 — Material corrections (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Fix material errors found in `0.6.x`.
|
||||
- [ ] Tag `v0.6.2` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix material errors found in `0.6.x`.
|
||||
|
||||
## 0.6.3 — Audio balancing (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Balance audio levels across the scene.
|
||||
- [ ] Tag `v0.6.3` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** balance audio levels across the scene.
|
||||
|
||||
## 0.7.0 — Settings, control rebinding, save/load, accessibility (weeks 25–27)
|
||||
|
||||
### Systems
|
||||
|
||||
- [ ] Build settings menu (graphics and audio options).
|
||||
- [ ] Implement control rebinding with persistence.
|
||||
- [ ] Implement save/load for player state.
|
||||
- [ ] Add accessibility basics (e.g. FOV, subtitle, colorblind options as applicable).
|
||||
|
||||
### Release checks
|
||||
|
||||
- [ ] Settings, rebinding, and save/load all persist across restarts.
|
||||
- [ ] No blocking UI defects.
|
||||
- [ ] Tag `v0.7.0` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** a settings menu for graphics and audio, control rebinding and save/load that persist across restarts, and accessibility basics (FOV, subtitles, colour-blind options where applicable).
|
||||
|
||||
## 0.7.1 — Save fixes (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Fix save/load defects.
|
||||
- [ ] Tag `v0.7.1` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix save and load defects.
|
||||
|
||||
## 0.7.2 — UI fixes (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Fix UI issues in `0.7.x`.
|
||||
- [ ] Tag `v0.7.2` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix UI issues in `0.7.x`.
|
||||
|
||||
## 0.7.3 — Input persistence fixes (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Fix rebinding persistence issues.
|
||||
- [ ] Tag `v0.7.3` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix rebinding persistence issues.
|
||||
|
||||
## 0.8.0 — Performance budgets and broader Linux hardware testing (weeks 28–30)
|
||||
|
||||
### Performance
|
||||
|
||||
- [ ] Define performance budgets (frame time, VRAM) and document them.
|
||||
- [ ] Profile the full neighborhood; fix budget violations.
|
||||
- [ ] Test on broader Linux hardware and VRAM configurations; document results.
|
||||
- [ ] Re-validate the 1440p quality preset with upscaling state recorded.
|
||||
|
||||
### Release checks
|
||||
|
||||
- [ ] Performance budgets met on documented hardware matrix.
|
||||
- [ ] No blocking compatibility defects.
|
||||
- [ ] Tag `v0.8.0` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** define frame-time and VRAM budgets, profile the full neighbourhood against them, test on a broader Linux hardware and VRAM matrix, and re-validate the 1440p quality preset with the upscaling state recorded.
|
||||
|
||||
## 0.8.1 — VRAM reductions (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Reduce VRAM usage where profiling shows excess.
|
||||
- [ ] Tag `v0.8.1` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** reduce VRAM usage where profiling shows excess.
|
||||
|
||||
## 0.8.2 — Frame-time fixes (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Fix frame-time spikes found in `0.8.x`.
|
||||
- [ ] Tag `v0.8.2` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix frame-time spikes found in `0.8.x`.
|
||||
|
||||
## 0.8.3 — Compatibility fixes (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Fix compatibility issues from the hardware testing matrix.
|
||||
- [ ] Tag `v0.8.3` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix compatibility issues from the hardware testing matrix.
|
||||
|
||||
## 0.9.0 — Feature freeze, public beta, license and build audit (weeks 31–34)
|
||||
|
||||
### Freeze and audit
|
||||
|
||||
- [ ] Freeze features; document the freeze in `CHANGELOG.md`.
|
||||
- [ ] Produce a public beta build for testers.
|
||||
- [ ] Complete license audit: `THIRD_PARTY_NOTICES.md`, all MPFB assets, textures, animations, vehicles, audio.
|
||||
- [ ] Review ODbL obligations for any distributed derived geographic database.
|
||||
- [ ] Review Epic distribution requirements before publishing.
|
||||
- [ ] Verify a fresh checkout builds and packages reproducibly.
|
||||
|
||||
### Release checks
|
||||
|
||||
- [ ] Beta feedback triaged; blockers logged for `0.9.x`.
|
||||
- [ ] Tag `v0.9.0` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** freeze features, produce a public beta for testers, and complete the audits: licences for `THIRD_PARTY_NOTICES.md` and all MPFB, texture, animation, vehicle, and audio assets; Epic distribution requirements; and a fresh checkout that builds and packages reproducibly.
|
||||
|
||||
## 0.9.1 — Blocker fixes (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Fix blockers reported during the public beta.
|
||||
- [ ] Tag `v0.9.1` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix blockers reported during the public beta.
|
||||
|
||||
## 0.9.2 — Regression fixes (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Fix regressions introduced by `0.9.x` fixes.
|
||||
- [ ] Tag `v0.9.2` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** fix regressions introduced by `0.9.x` fixes.
|
||||
|
||||
## 0.9.3 — Documentation fixes (patch)
|
||||
|
||||
### Patch tasks
|
||||
|
||||
- [ ] Update all `Docs/` for accuracy before `1.0.0`.
|
||||
- [ ] Tag `v0.9.3` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** update all `Docs/` for accuracy before `1.0.0`.
|
||||
|
||||
## 1.0.0 — Stable neighborhood-scale walking/driving release (weeks 35–40)
|
||||
|
||||
### Final release
|
||||
|
||||
- [ ] Complete final acceptance sweep across all `0.x` features.
|
||||
- [ ] Confirm the stable, agreed game scope is met (neighborhood-scale walking and driving).
|
||||
- [ ] Final build, documentation, and licensing verification.
|
||||
- [ ] Tag `v1.0.0` and write `CHANGELOG.md` entry; then remove this section.
|
||||
**Goal:** a final acceptance sweep across all `0.x` features, confirmation that the agreed scope (neighbourhood-scale walking and driving) is met, and final build, documentation, and licensing verification. `1.0.0` marks the stable scope, not completion of a whole city.
|
||||
+37
-107
@@ -1,8 +1,6 @@
|
||||
# Screenshots — visual review
|
||||
|
||||
Rendered in-game captures of the current project content, generated by
|
||||
[`Tools/render_screenshots.sh`](../Tools/render_screenshots.sh). The PNGs are committed alongside
|
||||
this README so a reviewer sees exactly what the build rendered.
|
||||
Rendered in-game captures of the current project content, generated by [`Tools/render_screenshots.sh`](../Tools/render_screenshots.sh). The PNGs are committed alongside this README so a reviewer sees exactly what the build rendered.
|
||||
|
||||
## How to regenerate
|
||||
|
||||
@@ -10,118 +8,50 @@ this README so a reviewer sees exactly what the build rendered.
|
||||
Tools/render_screenshots.sh
|
||||
```
|
||||
|
||||
The script rebuilds `Game/GigLine/Content/Test/ReviewMap.umap` from the current content, runs the
|
||||
game on it, lets `AGigLineShotActor` walk the fixed camera list, captures each shot, copies the
|
||||
PNGs here and exits. About a minute. The camera list lives in `Tools/ue_build_review_map.py`;
|
||||
add new shots there and delete retired ones from this folder.
|
||||
Rebuilds `Game/GigLine/Content/Test/ReviewMap.umap` from current content, runs the game on it (and on the DemoMap for the walkable views), walks the fixed camera list via `AGigLineShotActor`, captures each shot, copies the PNGs here and exits — about a minute. The camera list lives in `Tools/ue_build_review_map.py`; add new shots there, remove retired ones here.
|
||||
|
||||
> Naming: shots use their stable camera name (no index prefix), so adding or reordering cameras never renumbers files or orphans the old ones. Click an image for the full-size PNG.
|
||||
|
||||
## Shot list
|
||||
|
||||
All shots ☑ approved 2026-11-07, including the block frontage pass that added the neighbouring buildings and corrected the hero and north-building placement.
|
||||
|
||||
**2026-11-08 — regenerated set.** Re-rendered via `Tools/render_screenshots.sh` after the placement release check. The `demo-north-buildings` camera was re-pointed (it previously sat 9.5 km west and framed 85 % sky); that shot now shows both north buildings, and all maps carry the re-staged shot cameras. **Approved 2026-11-08** (owner review of the regenerated set).
|
||||
|
||||
| # | File | Preview | Map | View | What it shows |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 1 | `review-overview.png` | [<img src="review-overview.png" alt="overview" width="180">](review-overview.png) | Review | 7 m up, NE | Whole review layout: character + both cubes on the floor |
|
||||
| 2 | `review-character.png` | [<img src="review-character.png" alt="character" width="180">](review-character.png) | Review | eye height, south | MPFB mannequin, idle pose + warm skin; feet meet the plane |
|
||||
| 3 | `review-scale-cubes.png` | [<img src="review-scale-cubes.png" alt="scale cubes" width="180">](review-scale-cubes.png) | Review | south | Floating vs grounded cube: cast vs contact shadow |
|
||||
| 4 | `review-player-placement.png` | [<img src="review-player-placement.png" alt="player placement" width="180">](review-player-placement.png) | Review | spawn | The `AGigLineCharacter` pawn, capsule height and landing |
|
||||
| 5 | `review-npc-prefab.png` | [<img src="review-npc-prefab.png" alt="npc prefab" width="180">](review-npc-prefab.png) | Review | mid, west | The `AGigLineNPCPerson` prefab: collision capsule + visible MPFB body, feet on Z 0 |
|
||||
| 6 | `review-ground-contact.png` | [<img src="review-ground-contact.png" alt="ground contact" width="180">](review-ground-contact.png) | Review | knee height, feet | Floating/sinking character is obvious here |
|
||||
| 7 | `review-shadow-compare.png` | [<img src="review-shadow-compare.png" alt="shadow compare" width="180">](review-shadow-compare.png) | Review | low angle | Detached (floating) vs touching (grounded) shadows |
|
||||
| 8 | `review-hero-portals.png` | [<img src="review-hero-portals.png" alt="hero portals" width="180">](review-hero-portals.png) | Review | 8 m, eye height | Three genuine 1.2 m portal recesses with flights and plinth |
|
||||
| 9 | `review-hero-facade-front.png` | [<img src="review-hero-facade-front.png" alt="hero facade front" width="180">](review-hero-facade-front.png) | Review | road side, straight-on | Hero façade elevation and column rhythm |
|
||||
| 10 | `review-hero-facade-oblique.png` | [<img src="review-hero-facade-oblique.png" alt="hero facade oblique" width="180">](review-hero-facade-oblique.png) | Review | north-west | Body depth and column line standing proud of the mass |
|
||||
| 11 | `demo/review-demo-spawn-view.png` | [<img src="demo/review-demo-spawn-view.png" alt="spawn view" width="180">](demo/review-demo-spawn-view.png) | Demo | north sidewalk | The whole block: hero façade between its neighbours, both sidewalks and the road |
|
||||
| 12 | `demo/review-demo-street-level.png` | [<img src="demo/review-demo-street-level.png" alt="street level" width="180">](demo/review-demo-street-level.png) | Demo | eye height, north side | The hero colonnade with the adjacent frontage buildings either side |
|
||||
| 13 | `demo/review-demo-crossing.png` | [<img src="demo/review-demo-crossing.png" alt="crossing" width="180">](demo/review-demo-crossing.png) | Demo | along the crosswalk | The designated mid-block crossing at s=130 m (UE X -14956): three zebra bars on the asphalt, with the south frontage and the hero colonnade behind |
|
||||
| 14 | `demo/review-demo-north-buildings.png` | [<img src="demo/review-demo-north-buildings.png" alt="north buildings" width="180">](demo/review-demo-north-buildings.png) | Demo | across the Blvd | North-side buildings (TCL, Tussaud's) seen from the south side across the road. **2026-11-08:** camera re-pointed to the buildings' mass centre (was 9.5 km west → 85% sky); now frames both north buildings. Flat surfaces = `0.1.4` façade scope. |
|
||||
|
||||
## Scene conventions
|
||||
|
||||
- **Floor**: 160 m square, 10 cm thick, top face exactly at Z 0. Anything standing on it has bounds
|
||||
starting at Z 0; the character measures Z 0..83 cm.
|
||||
- **Cubes**: `Cube_ScaleTest_A` floats at Z 2 m, `Cube_ScaleTest_B` rests on the floor. Both are
|
||||
exactly 1 m, so they double as the scale reference.
|
||||
- **Lighting**: one warm directional key light plus a sky fill. This is the review rig, not the
|
||||
`0.1.0` daytime look.
|
||||
- **Known limitation**: outside the floor there is no geometry, so the horizon is a black band.
|
||||
|
||||
## Shots
|
||||
|
||||
## review-00-overview.png — Wide scene overview, approval: ☑ approved 2026-11-07
|
||||
|
||||

|
||||
|
||||
Character and both cubes on the 160 m floor, camera 7 m up looking north-east. Reads the whole review layout at once.
|
||||
|
||||
## review-01-character.png — MPFB mannequin, idle pose + warm skin, approval: ☐ pending (regenerated 2026-11-07)
|
||||
|
||||

|
||||
|
||||
The imported MPFB character at eye height from the south, in a relaxed standing idle pose (arms down, not A-pose) with a warm skin-tone diffuse instead of the grey mannequin. Check the feet meet the plane: the contact shadow joins the soles.
|
||||
|
||||
## review-02-scale-cubes.png — Scale cubes, approval: ☑ approved 2026-11-07
|
||||
|
||||

|
||||
|
||||
Both 1 m cubes from the south: the left one floats at 2 m, the right one rests on the floor. Compares cast shadow against contact shadow.
|
||||
|
||||
## review-03-player-placement.png — Player pawn placement, approval: ☑ approved 2026-11-07
|
||||
|
||||

|
||||
|
||||
The pawn the game actually spawns (`AGigLineCharacter`) at the spawn point, with the floating cube behind. Confirms capsule height and where the player lands.
|
||||
|
||||
## review-04-ground-contact.png — Ground contact detail, approval: ☑ approved 2026-11-07
|
||||
|
||||

|
||||
|
||||
Low camera, knee height, close on the mannequin's feet. A floating or sinking character is obvious here without measuring.
|
||||
|
||||
## review-04-hero-facade-front.png — review-04-hero-facade-front, approval: ☑ approved 2026-11-07
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## review-05-hero-facade-oblique.png — review-05-hero-facade-oblique, approval: ☑ approved 2026-11-07
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## review-05-shadow-compare.png — Shadow comparison, approval: ☑ approved 2026-11-07
|
||||
|
||||

|
||||
|
||||
Low angle, both cubes side by side with the character between them. The detached shadow of the floating cube against the grounded one.
|
||||
|
||||
## review-06-hero-portals.png — Hero façade portals, approval: ☑ approved 2026-11-07
|
||||
|
||||

|
||||
|
||||
New: generated 2026-11-07 after the portals were rebuilt. Eye height at 8 m, filling the frame with the three doorways, their flights, and the plinth. The doors are now genuine 1.2 m recesses cut through four plinth piers, with stone jambs and lintels standing proud and three mullions in each opening.
|
||||
|
||||
What this shot is evidence for: that the entrance is three separate openings with individual flights (not the inherited "eight steps, 12 m wide"), and that the facade stands on Z 0 rather than sinking into the ground.
|
||||
|
||||
## review-07-hero-facade-front.png — Hero façade, north sidewalk, approval: ☑ approved 2026-11-07
|
||||
|
||||

|
||||
|
||||
Closer straight-on view from the road side of the building line.
|
||||
|
||||
## review-08-hero-facade-oblique.png — Hero façade, oblique, approval: ☑ approved 2026-11-07
|
||||
|
||||

|
||||
|
||||
From the north-west, showing the 12 m body and the column line standing proud of the mass.
|
||||
- **Review map** (`/Game/Test/ReviewMap`): 160 m floor, top face at Z 0; the character measures Z 0..83 cm. One warm key light plus sky fill.
|
||||
- **Cubes**: `Cube_ScaleTest_A` rests on the floor, `Cube_ScaleTest_B` floats at Z 2 m; both are exactly 1 m — the scale reference.
|
||||
- **Demo map** (`/Game/Hollywood/DemoMap`): fixed daytime — sun at pitch −50 / yaw 120 (intensity 4, warm), sky light 2.6, sky atmosphere, height fog, fixed exposure.
|
||||
- **Materials**: every exported surface carries a look-developed material built in `Tools/ue_build_demo_map.py` (`ensure_material`) because Blender's flat diffuse colours do not survive the FBX → Unreal import. Each has an explicit roughness and a subtle world-space noise blend between two shades: `M_Asphalt` (0.92), `M_Concrete` (0.88), `M_KerbConcrete` (0.85), `M_Ground` (0.98), `M_RoadPaint` (0.62), `M_FacadeStone` (0.72) and `M_Stucco` (0.86) for the north-side buildings.
|
||||
- **Lighting is dynamic-only** (`r.AllowStaticLighting=False`, all lights Movable), so no lighting build is ever required.
|
||||
|
||||
## What to look for
|
||||
|
||||
- **Ground contact**: feet meet the plane with no gap and no shadow seam.
|
||||
- **Scale**: everything checks against the 1 m cubes.
|
||||
- **Façade proportions**: six columns at 4.96 m centres across 25.75 m; check the rhythm reads as the
|
||||
photographs do in silhouette.
|
||||
- **Façade rhythm**: six columns at 4.96 m across 25.75 m should read as the photographs in silhouette.
|
||||
|
||||
## Known cosmetic issues
|
||||
|
||||
- The floor renders with a faint speckle from the `Constant3Vector` wiring in
|
||||
`Tools/ue_build_review_map.py`. Cosmetic only.
|
||||
- The hero façade still has flat Blender colours; surfaces are a later materials pass.
|
||||
|
||||
## Demo map views (2026-11-07) — ☐ pending
|
||||
|
||||
Rendered from `/Game/Hollywood/DemoMap`, the walkable level rather than the review staging. Cameras are staged by `Tools/ue_build_review_map.py` and captured with the same shot actor.
|
||||
|
||||
| Shot | Shows |
|
||||
| --- | --- |
|
||||
| `demo/review-00-demo-spawn-view.png` | The player's own spawn view: north sidewalk, 20 m back, the full 14.5 m of the hero façade across the road |
|
||||
| `demo/review-01-demo-street-level.png` | Close street level: portals, flights, banners and the colonnade from the road |
|
||||
| `demo/review-02-demo-crossing.png` | From the west end looking along the block |
|
||||
| `demo/review-03-demo-north-buildings.png` | **New 2026-11-07:** the two north-side background buildings (TCL Chinese Theatre 6925, Madame Tussaud's 6933) seen from the south sidewalk across Hollywood Blvd — their frontage and mass. From the OSM blockout volumes, mirrored to the north side of the road. |
|
||||
|
||||
Known issues in this build, visible in the shots and already on the roadmap:
|
||||
|
||||
- The façade renders very pale — the Blender materials came across as flat colours with no normal or roughness, so it reads as untextured stone. That is the `0.1.4` façade patch, not a lighting bug.
|
||||
- The banners and window panels stand detached from the wall, floating in the colonnade. They are the next modelling pass.
|
||||
- Terrain carries the sidewalks between the kerb and the building line, so the walkable surface there is the terrain mesh, not a modelled pavement.
|
||||
|
||||
- Floor speckle from the `Constant3Vector` wiring in `Tools/ue_build_review_map.py`.
|
||||
- Hero façade renders pale — Blender flat colours with no normal/roughness, so it reads as untextured stone (the `0.1.4` façade patch, not a lighting bug).
|
||||
- Banners and window panels float detached from the colonnade wall (next modelling pass).
|
||||
- Between kerb and building line, the walkable surface is the terrain mesh, not a modelled pavement.
|
||||
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.
Loaded 100 of 120 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user