Conflicts resolved by union, not by picking a side: - AGENTS.md keeps the stronger visual-review rule (README updated with every visual change; a stale section is a defect). - render_screenshots.sh keeps the absolute engine path from main and adds -thirdperson from this branch - both were needed and neither alone worked. - Screenshots/README.md now lists six shots, retail and third-person both embedded; the known-issues tables merged, with the animation defect and unproven camera collision added to main's rows. - visual_targets.json carries both expectations. - Architecture docs: ADR-8 (realistic buildings) and ADR-9 (third-person body) both present and in numeric order; ADR-8 was mis-placed before the Quality Requirements heading in the combined document by an earlier edit, now fixed. - Binary conflicts resolved to main's content, since the retail trim only exists there. Checks: 82 tool tests, prettier clean, six embeds resolve.
102 lines
11 KiB
Markdown
102 lines
11 KiB
Markdown
# Tools
|
||
|
||
Repeatable helpers that drive Blender, Unreal, and the reference/asset sources. Nothing here is hand-run twice: every step that produced committed content has a script.
|
||
|
||
Geographic tools live in [`Geo/tools/`](../Geo/README.md) and the Blender-side builders live next to their output in `Art/blender/*/`; this folder holds everything that talks to Unreal, the network, or the repository.
|
||
|
||
## Conventions
|
||
|
||
- One script, one job. Every input is an argument; no interactive prompts.
|
||
- Absolute paths where a tool crosses into a sandbox (Blender Flatpak, the Unreal commandlet). Those processes do not share the repo's working directory.
|
||
- Unreal commandlets swallow `print()`, so each writes a result file under `/tmp/gigline/`. Read the file; do not trust the exit code alone.
|
||
- No credentials, no writes outside the project by default.
|
||
|
||
## Repository and assets
|
||
|
||
| Tool | Purpose |
|
||
| --- | --- |
|
||
| `check-lfs.sh` | Fails if the `lfs` filter is missing or an LFS-tracked file was committed as a plain blob |
|
||
| `facade_kit_build.py` | Builds the reusable façade **module kit** at the measured 4.96 m bay: pilaster, sill, lintel, window bay (surround + glazing), shopfront bay (piers, glass, fascia), door bay (jambs, door, threshold), cornice and parapet + coping. Exports each piece with the shared preset plus a JSON part report (dims + evidence per piece). |
|
||
| `ue_import_retail_detail.py` | Imports the two retail trim meshes into `/Game/Hollywood/Frontage` with complex-as-simple collision — the trim decorates a solid the player already collides with, so it must not add snag points. Staged by the demo builder as `RetailTrim_*`. |
|
||
| `ue_import_facade_kit.py` | Imports the kit into `/Game/Hollywood/Kit/*` as static meshes with complex-as-simple collision; writes `/tmp/gigline/kit-import.txt`. |
|
||
| `retail_frontage_build.py` | Retail frontage trim for **6800 Lee Drug** and **6806–6820** (0.1.6): bronze cornice, mural row, lettering band, stepped Deco parapet and rounded corner tower on Lee Drug; burgundy fascia, pilasters, white-framed windows and banded parapet on 6806–6820. Built in world coordinates from the blockout bounds, one joined mesh per building, JSON part report with the evidence behind every piece. `6822 Budget` is **not** built — no photograph exists. |
|
||
| `retail_frontage_build.py` | Retail frontage trim for **6800 Lee Drug** and **6806–6820** (0.1.6): bronze cornice, mural row, lettering band, stepped Deco parapet and rounded corner tower on Lee Drug; burgundy fascia, pilasters, white-framed windows and banded parapet on 6806–6820. Built in world coordinates from the blockout bounds, one joined mesh per building, JSON part report with the evidence behind every piece. `6822 Budget` is **not** built — no photograph exists. |
|
||
| `theatre_trim_build.py` | El Capitan Theatre trim: projecting marquee canopy, illuminated fascia, recessed soffit, vertical blade sign with frame and bracket. Built **in world coordinates** (span and pavement height baked in) with a JSON part report, so staging is a single spawn at the origin. |
|
||
| `ue_stage_theatre_trim.py` | Imports the trim to `/Game/Hollywood/Theatre` and stages one actor per piece in the DemoMap with site materials; clears a previous run first. |
|
||
| `ue_apply_facade_kit.py` | Lays the kit over a building's street face from its own bounding box: bay count = round(width / 4.96), storeys from the height, shopfronts on the ground floor, windows above, pilasters on bay boundaries, cornice and parapet at the top. Clears a previous application first; unique labels per building. |
|
||
| `generate_facade_textures.py` | Deterministic stdlib PNG generator for the facade maps (`T_StoneAshlar`, `T_Stucco`, `T_Asphalt`, `T_Concrete`, each `_BC` base colour + `_R` roughness). Pure-zlib writer, hash-based value noise, identical bytes on every run; `--size` and `--out` (`/tmp/gigline/textures` by default). `ue_build_demo_map.py` imports them into `/Game/Hollywood/Textures` and samples them world-aligned. |
|
||
| `ue_audit_scene.py` | Gate on the DemoMap: no unmaterialed actor, every verified frontage span within tolerance (`ue_placement`), no duplicate actor labels. Writes `/tmp/gigline/scene-audit.txt`; `render_screenshots.sh --verify` fails on a non-zero failure count. Catches the class of defect that still renders plausibly. |
|
||
| `ue_placement.py` | Single source of truth for where staged actors belong in the DemoMap (hero position, verified frontage spans) plus `span_error`/`describe_span_failure` validators. Exists because the hero position was written twice and drifted — the importer kept the pre-`0.1.0` value and moved the hero 13 m east onto the wrong side of the boulevard. No `unreal` import, so it is unit-tested. |
|
||
| `ue_common.py` | Shared helpers for the Unreal commandlet scripts: `make_writable()` clears the read-only bit an LFS `.umap` checks out with (otherwise `save_map()` returns False silently), and `require_saved()` makes that failure explicit. |
|
||
| `fbx_export_preset.py` | The **single** FBX export preset (`ue_metric_v1` / `ue_character_v1`): metres→centimetres via `FBX_SCALE_NONE`, axis mapping east +X / north −Y / up +Z, `mesh_smooth_type="FACE"` (smoothing groups survive the import). Every Blender exporter imports it instead of copying the argument block; `preset_kwargs()` is bpy-free and unit-tested. |
|
||
| `sibling_asset_audit.py` | Reads the sibling project's prop library from GLB headers into `Art/props/reuse-manifest.json`; copies nothing |
|
||
| `scale_test_export.py` / `ue_scale_test_import.py` | The 1 m = 100 uu scale test; the named presets `ue_metric_v1` live in these two files |
|
||
|
||
## Reference gathering
|
||
|
||
| Tool | Purpose |
|
||
| --- | --- |
|
||
| `commons_fetch.py` | Wikimedia Commons: search, exact `File:` titles, or a whole category; writes a provenance `sidecar.json` |
|
||
| `page_image_fetch.py` | Resolves a web page's `og:image` and downloads it (Flickr, Commons file pages, photo blogs) |
|
||
| `addon_compat_probe.py` | Verifies MPFB/Blosm can load and generate headless on the pinned Blender |
|
||
|
||
## Blender content
|
||
|
||
| Tool | Purpose |
|
||
| --- | --- |
|
||
| `hero_facade_build.py` | Parametric hero façade; emits a JSON report naming the evidence behind every part |
|
||
| `character_anim_build.py` | Authors the basic animation set on the MPFB rig — `GigLine_Idle` (60 f) and `GigLine_Walk` (30 f) sampled from parametric cycles at 30 fps — and exports one animated FBX with the `ue_character_v1` preset. Rotation axes are measured on the rig, not assumed (probe in the docstring). `--out`, `--report`. |
|
||
| `mpfb_character_export.py` | MPFB human, standard rig, baked skin, FBX export |
|
||
|
||
## Unreal import and level building
|
||
|
||
| Tool | Purpose |
|
||
| --- | --- |
|
||
| `ue_import_site.py` | Imports the six site FBXs into `/Game/Hollywood/Site`, setting collision complexity per mesh |
|
||
| `ue_hero_facade_import.py` | Imports the façade and places it on the measured building line |
|
||
| `ue_build_demo_map.py` | Builds `/Game/Hollywood/DemoMap`: lighting, façade, site meshes, player start |
|
||
| `ue_build_review_map.py` | Rebuilds `Test/ReviewMap`, stages the shot camera list, and stages the demo-map views |
|
||
| `ue_create_input_assets.py` | Creates `/Game/Input` actions and the mapping context headlessly |
|
||
| `ue_character_anim_import.py` | Imports the animated FBX into `/Game/Test/Character` and **verifies an AnimSequence exists** — an FBX exported without `bake_anim` imports a mesh and no clips, with no error. Writes `VERDICT: PASS/FAIL` to `/tmp/gigline/character-anim-import.txt`. |
|
||
| `ue_character_import.py` | Skeletal import plus the material/texture chain Interchange skips |
|
||
|
||
## Visual review
|
||
|
||
| Tool | Purpose |
|
||
| --- | --- |
|
||
| `render_screenshots.py` / `render_screenshots.sh` | Rebuilds the review map, runs the game, captures the shot list into `Screenshots/` |
|
||
| `judge_screenshot.py` | Vision-model pre-check: judges a screenshot against an expected-scene description (`--image` + `--expect`, or `--shots` for every shot in the README). Reads `OPENROUTER_API_KEY` from `.env` (ignored). Optional, advisory only — the model is unreliable on this content and never replaces a human verdict. |
|
||
| `visual_loop.py` | The reference-vs-render loop. `--verify` gates each capture against its expectation in `visual_targets.json` (blank/short/wrong-size/tone-less shots fail); `--compare` measures luminance, contrast, edge density, palette and 3×3 composition against the reference photograph and turns the deltas into modelling actions naming the skill to consult (`blender-modeler`, `archviz`, `materials`, `lookdev`, `camera-cinematography`); `--iteration N` writes the artefacts under `Art/reference/iterations/N/`, which is **gitignored until the owner confirms a shot**. |
|
||
| `visual_targets.json` | Per-shot expectation and reference photograph for `visual_loop.py`. Every entry must be a demo-map shot. |
|
||
|
||
## Gameplay QA
|
||
|
||
| Command | Purpose |
|
||
| --- | --- |
|
||
| `UnrealEditor <uproject> /Game/Hollywood/DemoMap -game -qaselftest` | Headless behaviour check: drives forward input for 2 s, then resets and pauses. Logs `GigLine QA: ... PASS/FAIL` lines and a final `RESULT`. Runs only with `-qaselftest`, so normal play is unaffected. |
|
||
| `-qaselftest` phases | Walk, reset, pause/unpause, a 16-row seam transect (0 holes), a ground-referenced wall probe (0/35) and a **facade occlusion probe** that fires sideways from the road centre and requires a blocking wall wherever a building is staged (0/9), plus a frame-time capture writing `Saved/Profiling/perf-walk-frametimes.csv` and logging `GigLine PERF:`. |
|
||
| `gigline.SetupLogs` cvar | Spawn/input setup logging, **off by default** (`DefaultEngine.ini [ConsoleVariables]`); enable at runtime with `gigline.SetupLogs 1`. |
|
||
|
||
## Reference and concept generation
|
||
|
||
| Tool | Purpose |
|
||
| --- | --- |
|
||
| `facade_detail_concepts.py` | Generates **frontal building plates** (whole elevation, entrance in its facade, or building with neighbours) from a local reference photograph with an image model (OpenRouter); close-up detail crops are deliberately not requested because they cannot be measured for proportion. `--list` is free and shows which studies have their reference present; `--all`/`--only <name>` generates. Never overwrites (writes `-v2`, `-v3`), writes a `.refgen.json` sidecar per image (model, prompt, source photo, SHA-256), and puts everything under `Art/reference/facade-details/` — gitignored, local-only, never shipped. Costs money per image, so it is never automatic. |
|
||
| `judge_screenshot.py` | (see Visual review) |
|
||
|
||
## Input injection
|
||
|
||
| Tool | Purpose |
|
||
| --- | --- |
|
||
| `input_emulation.py` | Pure-stdlib `ctypes`/`fcntl` driver that creates a virtual keyboard+mouse on `/dev/uinput`. The desktop treats it as real hardware, so it proves whether OS input actually reaches the game. Needs `/dev/uinput` writable (ACL on this host). |
|
||
| `input_test.py` | Injects a scripted sequence into the focused window: `--sequence move:2,look:3,jump:1,reset:1,pause:1,click`. |
|
||
|
||
## Tests
|
||
|
||
```bash
|
||
python3 -m pip install -r requirements-tools.txt
|
||
python3 -m unittest discover -s Tools/tests -t Tools -p 'test_*.py'
|
||
```
|
||
|
||
Stdlib `unittest`, no Blender/Unreal process. See [`tests/README.md`](tests/README.md): test the pure parts (encoding, classification, maths, parsing); if logic is buried in `main()`, extract it into a function so it can be tested.
|