Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d872034388 | ||
|
|
d8cfddbf89 | ||
|
|
d513e0b82c | ||
|
|
7efb5024ec | ||
|
|
a231f7588a | ||
|
|
3989d7b2e5 | ||
|
|
0eb17e3bb4 | ||
|
|
a7daae8a14 | ||
|
|
3f4a8e5f9f | ||
|
|
91b10e0f94 | ||
|
|
4943dbff16 | ||
|
|
b24d0aeb61 |
No files matched your search
@@ -84,19 +84,19 @@ jobs:
|
||||
# NOTE: on Linux, makensis uses -D defines (the /D form is Windows-only
|
||||
# and is rejected as a script path), and it resolves relative paths
|
||||
# against the script directory — pass absolute paths from the workspace.
|
||||
cp assets/textures/icon.ico packaging/windows/step-line.ico
|
||||
cp assets/textures/icon.ico packaging/windows/gig-line.ico
|
||||
makensis \
|
||||
-DVERSION="$VERSION" \
|
||||
-DARTIFACT="$PWD/build/step-line-windows-$VERSION.exe" \
|
||||
-DICON="$PWD/packaging/windows/step-line.ico" \
|
||||
-DOUT="$PWD/build/step-line-setup-$VERSION.exe" \
|
||||
packaging/windows/step-line.nsi
|
||||
-DARTIFACT="$PWD/build/gig-line-windows-$VERSION.exe" \
|
||||
-DICON="$PWD/packaging/windows/gig-line.ico" \
|
||||
-DOUT="$PWD/build/gig-line-setup-$VERSION.exe" \
|
||||
packaging/windows/gig-line.nsi
|
||||
|
||||
- name: Verify Windows installer contents
|
||||
run: |
|
||||
VERSION="$(sed -n 's/.*config\/version="\([^"]*\)".*/\1/p' project.godot)"
|
||||
ls -la build/step-line-setup-$VERSION.exe
|
||||
7z l build/step-line-setup-$VERSION.exe | grep -E "step-line.exe|uninstall.exe"
|
||||
ls -la build/gig-line-setup-$VERSION.exe
|
||||
7z l build/gig-line-setup-$VERSION.exe | grep -E "gig-line.exe|uninstall.exe"
|
||||
|
||||
- name: Package Linux AppImage
|
||||
run: |
|
||||
@@ -106,7 +106,7 @@ jobs:
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: step-line-build
|
||||
name: gig-line-build
|
||||
path: build/*
|
||||
if-no-files-found: error
|
||||
|
||||
|
||||
@@ -44,3 +44,23 @@ user://
|
||||
build/
|
||||
|
||||
.env
|
||||
|
||||
# AI / asset-development outputs (regenerated by the tools; not shipped in the game)
|
||||
assets/models/concepts/
|
||||
assets/models/npc/*.blend1
|
||||
assets/textures/npc/ai/
|
||||
assets/textures/ai/*
|
||||
!assets/textures/ai/game_ready/
|
||||
# Godot extracts embedded glTF textures next to the source on import
|
||||
assets/models/generated/npc_base_*_albedo.png
|
||||
assets/models/generated/npc_base_*_normal.png
|
||||
|
||||
# Local AppImage extraction
|
||||
squashfs-root/
|
||||
# Blender asset library: per-asset Cycles renders are local review output (the
|
||||
# committed review artefacts are docs/blender_previews/sheet_*.jpg), and Godot
|
||||
# extracts the GLB-embedded textures next to the models on import.
|
||||
docs/blender_previews/*.png
|
||||
assets/models/blender/**/*.png
|
||||
!assets/models/blender/**/*_ao.png
|
||||
assets/models/blender_src/*.blend1
|
||||
@@ -163,7 +163,7 @@ Completed task sections moved from `TODO.md`. Sections are moved only when every
|
||||
applied on phase change and shift start.
|
||||
- [x] Street lamps: poles with emissive bulbs + OmniLights on the sidewalk;
|
||||
`WorldBuilder.set_night_mode` switches bulbs/lights on at night.
|
||||
- [x] Marquee signage: emissive “STEP LINE” strips + Label3D above the entrance.
|
||||
- [x] Marquee signage: emissive “GIG LINE” strips + Label3D above the entrance.
|
||||
- [x] Street dressing: crosswalk stripes, planters, and low-poly trees.
|
||||
- [x] NPC visual variety: per-seed height/brightness variation (deterministic LCG).
|
||||
- [x] Denser routine traffic (tourists/performers/influencers/trespassers).
|
||||
@@ -211,21 +211,21 @@ Completed task sections moved from `TODO.md`. Sections are moved only when every
|
||||
- [x] Export presets: `binary_format/embed_pck=true` on all platforms — every binary is now
|
||||
self-contained (all game data in one file; `.pck` no longer shipped).
|
||||
- [x] App icon: `assets/textures/icon.png` (1024) + multi-size `icon.ico`, copied to
|
||||
`packaging/windows/step-line.ico` and `packaging/linux/step-line.png` via the generator.
|
||||
- [x] NSIS script `packaging/windows/step-line.nsi` (installs `Step Line.exe`, uninstaller,
|
||||
`packaging/windows/gig-line.ico` and `packaging/linux/gig-line.png` via the generator.
|
||||
- [x] NSIS script `packaging/windows/gig-line.nsi` (installs `Gig Line.exe`, uninstaller,
|
||||
shortcuts, registry entry; `makensis /DVERSION=… /DARTIFACT=…`).
|
||||
- [x] AppImage: `packaging/linux/step-line.desktop`, `AppRun`, appdata.xml, and
|
||||
- [x] AppImage: `packaging/linux/gig-line.desktop`, `AppRun`, appdata.xml, and
|
||||
`build_appimage.sh` (appimagetool, `APPIMAGE_EXTRACT_AND_RUN=1`).
|
||||
- [x] Local end-to-end: rebuilt embedded exports and produced `step-line-0.2.0-x86_64.AppImage`,
|
||||
- [x] Local end-to-end: rebuilt embedded exports and produced `gig-line-0.2.0-x86_64.AppImage`,
|
||||
ran it headless (0 script errors).
|
||||
- [x] CI: NSIS + AppImage steps added to `.gitea/workflows/build.yml` build job; publish glob
|
||||
already uploads `build/step-line-*<ver>*` (covers setup/AppImage names).
|
||||
- [x] CI run 1110 verified end-to-end (v0.2.1): NSIS installer `step-line-setup-0.2.1.exe`
|
||||
built, `7z l` shows `step-line.exe` + `uninstall.exe`, AppImage built (33.9 MB),
|
||||
already uploads `build/gig-line-*<ver>*` (covers setup/AppImage names).
|
||||
- [x] CI run 1110 verified end-to-end (v0.2.1): NSIS installer `gig-line-setup-0.2.1.exe`
|
||||
built, `7z l` shows `gig-line.exe` + `uninstall.exe`, AppImage built (33.9 MB),
|
||||
release `v0.2.1` published, `Job succeeded`.
|
||||
- Gotchas fixed on the way: makensis needs `-D` defines (not `/D`) and absolute paths
|
||||
(it resolves relatives against the script dir); installed exe must be space-free
|
||||
(`step-line.exe`); icon.ico must use classic BMP entries (ImageMagick; PIL emits
|
||||
(`gig-line.exe`); icon.ico must use classic BMP entries (ImageMagick; PIL emits
|
||||
PNG-compressed ICOs NSIS rejects).
|
||||
|
||||
## 🎮 Assets & detailed graphics pass (textures, rigs, UI feedback)
|
||||
@@ -234,7 +234,7 @@ Completed task sections moved from `TODO.md`. Sections are moved only when every
|
||||
seamless concrete, asphalt, awning, skyline, vignette, HUD icons, access-card art — all
|
||||
≤1024px and power-of-two; imported under `assets/textures/`.
|
||||
- [x] Environment: textured facade + glass door, concrete sidewalk, asphalt street, striped
|
||||
awning with “STEP LINE”, generated skyline backdrop planes, soft sun shadows.
|
||||
awning with “GIG LINE”, generated skyline backdrop planes, soft sun shadows.
|
||||
- [x] NPCs: humanoid rig (torso/head/limbs) with archetype props (camera, delivery box, hat,
|
||||
sign, backpack), procedural walk bob, head-turns-toward-guard cue.
|
||||
- [x] UI/feedback: inspection artifact cards, mode icons (flashlight/door/wand), escalation
|
||||
@@ -412,7 +412,7 @@ Completed 2026-10-31 (working tree). Headless **88/88**; `src/test/smoke_session
|
||||
offset and the spawn `y`, and verify eye height while walking the steps.
|
||||
- [x] Steps and the landing are visual-only (no collider). The guard walks through them; add
|
||||
simple step colliders or restage the landing so it does not intersect the walk path.
|
||||
- [x] Marquee text (`STEP LINE`) is tiny from the sidewalk. Scale the Label3D up.
|
||||
- [x] Marquee text (`GIG LINE`) is tiny from the sidewalk. Scale the Label3D up.
|
||||
- [x] Focus circle and name tag are small at 2–3 m. Consider a larger circle / font after a
|
||||
real playtest.
|
||||
- [x] NPC crowd cap (`MAX_LIVE_NPCS`) silently drops routine traffic at 16. Add a short
|
||||
@@ -655,7 +655,7 @@ produces a 63 MB static bundle and it serves locally with HTTP 200s for html/was
|
||||
|
||||
- [x] Add a `Web` preset to `export_presets.cfg` using `variant/thread_support=false` so the
|
||||
build runs on plain static hosting without COOP/COEP headers.
|
||||
- [x] Export to `build/web/` and package `build/step-line-web-<version>.zip` from
|
||||
- [x] Export to `build/web/` and package `build/gig-line-web-<version>.zip` from
|
||||
`tools/build_releases.sh` (zip or Python fallback).
|
||||
- [x] Publish the web zip with the desktop artifacts (`tools/publish_release.sh`).
|
||||
- [x] Cover the preset, the non-threaded choice, and the release wiring in
|
||||
@@ -814,3 +814,67 @@ lookup, constants, gamepad bindings, and settings persistence.
|
||||
deadzone, remap descriptor/apply/labels, and settings persistence for camera and bindings.
|
||||
- [x] Added `smoke_movement.gd` (windowed) and `11-precision-walk.png` with a movement catalogue
|
||||
entry, AI mockup, and compare coverage.
|
||||
|
||||
## NPC Facing During Movement
|
||||
|
||||
Completed 2026-11-01 (post-v0.5.0, pre-v0.6.0). Headless suite **209/209**.
|
||||
|
||||
- [x] Root cause: `NpcAvatar` used Godot's `look_at()`, which aims the node's **-Z** axis at
|
||||
the target, but the procedural rig's face (eyes, brows, mouth, focus disc) and every
|
||||
Blender-authored successor face **+Z**. Every walking NPC therefore showed its face
|
||||
toward the side it was leaving.
|
||||
- [x] Added the pure `NpcAvatar.yaw_towards(direction)` helper (+Z forward, tested at +Z/+X/-Z
|
||||
and an arbitrary direction), and replaced `look_at()` in the navigation and direct-move
|
||||
paths plus the reaction-face turn.
|
||||
- [x] Corrected the head-turn offsets to use the same +Z convention (`basis.z` instead of
|
||||
`-basis.z`), so noticed NPCs still look at the guard with the fixed facing.
|
||||
- [x] Added `test_yaw_faces_the_movement_direction`; recaptured the targeting screenshot to
|
||||
confirm NPCs now show their back only when walking away.
|
||||
|
||||
## Blender Asset Library Integration
|
||||
|
||||
Completed 2026-11-01 (v0.6.0 work). Headless **222/222**, Python **68/68**, all windowed/headless
|
||||
smokes pass, `compare` **11 good / 0 bad**. AO/ORM baking remains in Phase 4.
|
||||
|
||||
- [x] Committed the audited library (8 characters, 18 gear, 20 props), generator, `.blend`
|
||||
sources, manifest, review sheets, and integration notes.
|
||||
- [x] `NpcAvatar` selects a character variant by role/seed (`npc_casual`, `npc_jacket`,
|
||||
`npc_coat`, `npc_vest`, `npc_dress`, `npc_hoodie`, `npc_firefighter`) with `npc_base` and
|
||||
the procedural rig as fallbacks; the choice is recorded in `character_model`.
|
||||
- [x] Only the five contract slots (`skin`, `cloth`, `pants`, `accessory`, `hair`) are tinted;
|
||||
fixed detail materials keep their authored colour. `npc_vest` keeps its fixed hi-vis vest,
|
||||
and firefighter/guard use dark uniform tints.
|
||||
- [x] Library gear replaces the procedural accessories and hand items at the documented
|
||||
`head` / `back` / `chest` / `hand` attach points (cap, hood, helmet, backpack, SCBA tank,
|
||||
lanyard, phone, cup, clipboard, umbrella, camera, sign, flashlight, wand, radio), with the
|
||||
primitive path retained as fallback.
|
||||
- [x] `WorldBuilder` loads the Blender replacements (`bollard`, `bench`, `trash_bin`,
|
||||
`planter_box`, `newspaper_box`, `fire_hydrant`, `street_lamp`, `street_tree`,
|
||||
`traffic_cone`) and places the new dressing (`palm_tree`, `round_planter`, `a_frame_sign`,
|
||||
`crowd_barrier`, `stanchion`, `security_podium`, `handrail`,
|
||||
`double_door`, `marquee`, `sedan`, `wall_lantern`) with colliders/avoidance and placement
|
||||
validation.
|
||||
- [x] Library emissive surfaces (lamp bulbs, marquee bulbs/sign face, wall lanterns, door LED,
|
||||
sedan lights) are collected and driven by `set_night_mode()`.
|
||||
- [x] Added a library triangle counter and 60k triangle budget check; the current set is
|
||||
~27,986 triangles. NPC budget smoke: 16 avatars = 112 meshes at ~6.9 ms.
|
||||
- [x] Recaptured the README/catalogue screenshots (including the prop gallery from the library
|
||||
models) and fixed the marquee text placement.
|
||||
|
||||
## Environment and Lighting Lift
|
||||
|
||||
Completed 2026-11-01 (v0.6.0). Headless **222/222**, Python **69/69**, all smokes pass,
|
||||
`compare` **11 good / 0 bad**.
|
||||
|
||||
- [x] Baked per-asset AO maps for the full Blender library with `tools/bake_ao.py` (Cycles,
|
||||
256×256, stored next to each GLB as `<name>_ao.png`); Godot wires the red channel into
|
||||
`StandardMaterial3D.ao_texture` for character slots, gear and props.
|
||||
- [x] Regenerated brick, concrete and asphalt at 1024² through `asset_pipeline.py`, derived
|
||||
tangent normal maps with `tools/make_normal_maps.py`, and wired them into
|
||||
`SurfaceMaterials.textured()` while keeping the existing UV scales.
|
||||
- [x] Evaluated Forward+: it boots on the desktop Vulkan driver, but the renderer is a
|
||||
project-wide setting and the Web build must stay on GL Compatibility. Decision: keep
|
||||
Compatibility for v0.6.0; a desktop-only preset needs a separate project/override and is
|
||||
deferred.
|
||||
- [x] Recaptured `docs/screenshots/` and ran `compare` + `judge`; no regressions.
|
||||
- [x] Fixed the library marquee text/face alignment and added the library AO/triangle tests.
|
||||
@@ -4,6 +4,13 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
|
||||
|
||||
## Decisions
|
||||
|
||||
- Graphics v0.6.0 plan (2026-11-01): NPCs move to Blender-authored skinned meshes
|
||||
(`docs/plan-graphics-v0.6.md`, ADR-0013). `tools/npc_pipeline.py` builds
|
||||
`assets/models/generated/npc_base.glb`; `NpcAvatar` keeps the procedural `_build_body()`
|
||||
fallback. Preserve `Rig/Head`, the limb node names, and the seed-deterministic
|
||||
`outfit_for`/`layer_for`/`hand_item_for` helpers. Blender = flatpak 5.2.0 LTS, sandbox cwd
|
||||
`/app/blender` → absolute paths for `--python`. CI stays offline (committed assets).
|
||||
|
||||
- Engine: Godot 4.3 stable (official editor build, `~/.local/bin/godot`). The system's preinstalled `godot-runner` (4.7.2, Fedora) cannot load projects from arbitrary paths because it was compiled without path overrides; use the official editor binary instead.
|
||||
- Language: GDScript. All cross-file references use explicit `preload()` constants (not `class_name` globals) so scripts compile in headless `--script` runs without the editor's class cache.
|
||||
- Test framework deviation: the project uses a custom minimal headless test runner (`src/test/test_runner.gd`) instead of GUT, because GUT needs the editor plugin. CI runs it with `--headless`.
|
||||
@@ -13,8 +20,78 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
|
||||
- Shift length is the scenario's `shift_duration_s` (900 s in all three venues). Tests and the screenshot tool override elapsed time; they do not change the default.
|
||||
- RNG: NPC reactions and event scheduling are seedable for deterministic tests.
|
||||
|
||||
- **Blender invocation (v0.6.0):** `flatpak run org.blender.Blender --background --python
|
||||
/run/media/system/DATA/Documents/06-games/security-game/tools/npc_pipeline.py -- build`.
|
||||
The flatpak cannot read `/tmp`; use absolute repo paths. `assets/models/npc/.gdignore` is
|
||||
required, otherwise Godot's editor tries to import the committed `.blend` and hangs during
|
||||
`--import` (also add `.gdignore` to local build/squashfs folders when scanning is slow).
|
||||
- **Skinned NPC integration (v0.6.0):** `NpcAvatar._build_skinned_body()` loads
|
||||
`npc_base.glb` and keeps `_build_body()` as fallback (`use_skinned_model=false` in tests).
|
||||
Bone pose rotation is the **absolute local orientation**, not an offset from rest: compose with
|
||||
`get_bone_rest(i).basis` or the whole body snaps to a broken pose. Proxies named Hip/Knee/
|
||||
Shoulder/Elbow/Hand + Head marker preserve the old animation math; `Rig/Head` is the marker.
|
||||
Five material slots are tinted via `SurfaceMaterials.npc_slot()`; keep `_rig.scale` uniform on
|
||||
the skinned path. Budget: 16 avatars = **144 meshes**, ~6.9 ms headless.
|
||||
- **NPC facing (fixed after v0.5.0):** the avatar's visual front is **+Z** (procedural eyes
|
||||
and the Blender model's face). Godot `look_at()` aims **-Z**, which made every NPC walk
|
||||
backwards. Use `NpcAvatar.yaw_towards(dir)` and set `rotation.y`; do not call `look_at()` on
|
||||
avatars. Head-turn offsets use `+basis.z` for the same reason. Covered by
|
||||
`test_yaw_faces_the_movement_direction`.
|
||||
|
||||
## Handoff (read this first)
|
||||
|
||||
- **Visual-alignment pass planned (2026-11-01):** `TODO.md` gains a "Visual
|
||||
alignment with rendered mockups" section. Goal: restructure generated-image
|
||||
storage, move the reference mockups out of `docs/screens/designs/` into a
|
||||
runtime-accessible root (e.g. `assets/images/`), load the proposed UI/scene
|
||||
art at runtime (start menu, selection screens, HUD, panels), and make the game
|
||||
match the mockups — verified by `screen_designs.py compare` + `judge`. Not
|
||||
started; no code moved yet.
|
||||
|
||||
- **Phase 2 — AI texturing (this agent, DONE 2026-11-01).** Phase 1 assets were
|
||||
already in the tree (untracked) and are preserved. Generated six seamless AI
|
||||
clothing albedos into `assets/textures/npc/ai/{shirt,trousers,jacket,vest,
|
||||
coat,dress}.png` (512×512, `--role tile`, gpt-5-image-mini; audit 6 good).
|
||||
**Gotcha:** `asset_pipeline.py generate` defaults to `GEN_SIZE=2048`, which
|
||||
gpt-5-image-mini rejects (HTTP 400); pass `--request-size 1024` for 1:1 roles.
|
||||
`generate --role tile` already applies `make_seamless`, so a separate
|
||||
`seamless` pass is redundant. Added to `tools/npc_pipeline.py`: `prepare`
|
||||
(layers AI shirt→cloth, trousers→pants albedos at 128px) and `palette`
|
||||
(writes `assets/textures/npc/palette.json` via `model_pipeline.dominant_colors`).
|
||||
`write_slot_textures()` now skips the albedo for slots with an AI override
|
||||
(normal/ORM stay procedural). Blender `build` re-run: GLB audit ok, 1204 tris /
|
||||
2488 verts, AI albedo embedded for cloth+pants. **Do not commit yet** — Phase 1
|
||||
files are uncommitted; coordinate the commit with the Phase 1/3 agents.
|
||||
**Import concurrency:** a parallel agent is running
|
||||
`timeout 300 godot --headless --editor --quit --path .` and then checks
|
||||
`assets/models/generated/npc_base.glb.import` — treat that as the canonical
|
||||
import. My own `godot --headless --path . --import` hung with no output past
|
||||
the engine banner (two Godot instances contended on `.godot`); I killed the
|
||||
orphan (PID 2482934). Do not run a second import while that `--editor --quit`
|
||||
is active.
|
||||
Next: Phase 3 (`NpcAvatar._load_model` + slot tinting) consumes
|
||||
`palette.json` and the GLB; Phase 2's capture/judge item is blocked on that.
|
||||
|
||||
- **Git hygiene + tooling (2026-11-01):** dev asset folders are now gitignored
|
||||
and untracked: `assets/models/concepts/`, `assets/models/npc/` (Blender
|
||||
sources), `assets/textures/npc/ai/` (raw AI fabrics), and the raw
|
||||
`assets/textures/ai/*` outputs (only `assets/textures/ai/game_ready/` stays).
|
||||
Kept in the repo: `assets/textures/npc/*_{albedo,normal,orm}.png` +
|
||||
`palette.json`, `assets/models/generated/*` (props + npc_base.glb), and the
|
||||
authored `assets/textures/*` set. `tools/asset_pipeline.py generate` gained
|
||||
`--variants N` — generates N alternatives saved as `<stem>-1..N.png` (N=1
|
||||
keeps the unsuffixed path). Python tooling tests still pass (exit 0).
|
||||
|
||||
- **Screenshots + docs (2026-11-01):** regenerated the eleven README captures
|
||||
(`capture_screenshots.gd` + `capture_prop_gallery.gd`) — ten changed, the
|
||||
prop gallery is byte-identical (props unchanged). README gained an NPC
|
||||
character-model subsection, a "Confirmed game assets" section (linked
|
||||
`game_ready` textures, generated models, NPC maps), and refreshed
|
||||
dev-status / not-yet lines; `docs/architecture.md` Level 2 now lists
|
||||
`npc_pipeline.py` + `asset_pipeline.py` and the confirmed-asset locations.
|
||||
**Foreign, left uncommitted:** a TODO section "Bug Fix NPCs walking backwards"
|
||||
from another agent — do not touch or commit it.
|
||||
|
||||
- Repo: `/run/media/system/DATA/Documents/06-games/security-game` (also reachable at
|
||||
`/var/home/zwitschi/Documents/06-games/security-game`). Godot: `~/.local/bin/godot` (4.3.stable).
|
||||
Tests: `godot --headless --path . --script res://src/test/test_runner.gd` → **177/177**.
|
||||
@@ -29,7 +106,7 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
|
||||
aim-free selection with wheel cycling and Shift precision walk (ADR-0011/0012), and a **Web**
|
||||
export target. `export_presets.cfg` gains a `Web` preset with `variant/thread_support=false`
|
||||
(static hosting, no COOP/COEP); `tools/build_releases.sh` exports `build/web/` and zips it to
|
||||
`build/step-line-web-<ver>.zip`, and `publish_release.sh` uploads it. Covered by
|
||||
`build/gig-line-web-<ver>.zip`, and `publish_release.sh` uploads it. Covered by
|
||||
`src/test/tests/test_web_export.gd`.
|
||||
- **Prop atlas (v0.3.1):** `model_pipeline.atlas_for()` builds a per-prop power-of-two PNG with
|
||||
one 8×8 cell per palette role, plus a metallic/roughness atlas (G=roughness, B=metallic). The
|
||||
@@ -52,6 +129,33 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
|
||||
flat plane, so avoidance (not a baked mesh) routes guests around props. Placements live in
|
||||
`PROP_PLACEMENTS` with `placement_problems()` validation; variants come from `variant_for(index)`.
|
||||
`smoke_props.gd` must be windowed because the guard reads captured-mouse input.
|
||||
- **Blender asset library (v0.6.0, integrated 2026-11-01):**
|
||||
`tools/blender_assets.py` (`build`/`render` in Blender; `audit`/`sheet`/`list` stdlib) writes
|
||||
`assets/models/blender/{npc,npc_gear,props}/*.glb`, `manifest.json` (integration source of
|
||||
truth), sources in `assets/models/blender_src/` (`.gdignore`d), and review sheets
|
||||
`docs/blender_previews/sheet_*.jpg` (per-asset PNG renders are gitignored). 8 characters, 18
|
||||
gear, 20 props; notes in `docs/blender-assets.md`. Characters are drop-in for `npc_base`
|
||||
(same mesh node name, bones at the same rest positions, five tint slots) and stay under the
|
||||
6000/4000 budget only because of body decimation (0.36) + `smart_project` at 80°: glTF vertex
|
||||
count includes UV-seam splits, so extra beveled detail quickly breaks 4000.
|
||||
**Gotchas:** `bisect_plane` and thin bevels leave zero-area triangles (Godot warns "non-finite
|
||||
normal in LOD generation"); `_clean()` + a weld after bisect fix it, and the audit rejects them.
|
||||
Fixed-colour detail materials carry a 4×4 texture because `_apply_outfit_materials()` sets
|
||||
`albedo_color = WHITE` on unknown slots. Cycles CPU render ≈ 3 s/asset; Godot import of the
|
||||
library is clean.
|
||||
**Integration:** `NpcAvatar.character_model_for()` selects a variant by role/seed; only the
|
||||
five contract slots are tinted (detail materials untouched; `npc_vest` keeps its fixed hivis
|
||||
vest, firefighter/guard get dark uniform tints). Gear is attached at head/back/chest/hand and
|
||||
falls back to primitives; `npc_hoodie` skips the extra hood gear. `WorldBuilder` prefers
|
||||
`assets/models/blender/props/` for the six replacements and the new street dressing, fits the
|
||||
same colliders/obstacles, and collects library emissives for `set_night_mode()`.
|
||||
`LIBRARY_TRIANGLE_BUDGET` = 60k (current ~27,986). Library props are excluded from
|
||||
`PROP_PLACEMENTS` only when they need dedicated slots (door/marquee/sedan/handrail/lantern).
|
||||
- **v0.6.0 environment pass:** `tools/bake_ao.py` bakes 256² AO maps per library asset
|
||||
(`<name>_ao.png`, red channel) and `Materials.apply_ao()`/`SurfaceMaterials` wire them in;
|
||||
`tools/make_normal_maps.py` derives tangent normals for the 1024² brick/concrete/asphalt
|
||||
textures. Forward+ boots on desktop but is rejected for v0.6.0 (project-wide renderer, Web
|
||||
must stay GL Compatibility). Headless 222/222, Python 69/69.
|
||||
- **Prop pipeline (done):** audit enforces 400 tris / 600 verts, ground contact, UV range, no
|
||||
degenerate tris, one material. `capture_prop_gallery.gd` writes `10-prop-gallery.png` (3x2 studio
|
||||
line-up, back row on a platform) and the `prop_gallery` catalogue entry compares it.
|
||||
@@ -295,7 +399,7 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
|
||||
and published — installers/AppImage are on the releases page. Releasing = bump version → tag → push.
|
||||
- NSIS gotchas (validated in a local ubuntu:24.04 docker repro): makensis uses `-D` defines
|
||||
(never `/D`), resolves relative `-DICON`/`-DARTIFACT` against the **script directory** so CI
|
||||
passes `$PWD`-absolute paths; installed exe must be space-free (`step-line.exe`); ICO must be
|
||||
passes `$PWD`-absolute paths; installed exe must be space-free (`gig-line.exe`); ICO must be
|
||||
classic BMP entries (ImageMagick `icon:auto-resize=48,32,16`; PIL emits PNG-compressed ICOs NSIS
|
||||
cannot load).
|
||||
- appimagetool runs system `appstreamcli` if present (strict, exit 3 on warnings) — project
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Game Plan: Step Line
|
||||
# Game Plan: Gig Line
|
||||
|
||||
## Premise
|
||||
|
||||
@@ -399,6 +399,27 @@ The studio also has authorized guests and staff. They must either be recognized
|
||||
- Someone presents a counterfeit wristband or credential.
|
||||
- A violent threat appears during a crowded arrival, requiring the player to protect the entrance while calling for assistance.
|
||||
|
||||
---
|
||||
|
||||
## Visual Quality Direction
|
||||
|
||||
The game prioritizes readable, well-lit, low-poly presentation over realism (GL Compatibility
|
||||
renderer). The v0.6.0 visual pass moves presentation to a **Blender-authored asset library**:
|
||||
skinned NPC character variants, head/back/chest/hand gear, and street/venue props, all carrying
|
||||
baked albedo/normal/AO/ORM maps. A generator (`tools/blender_assets.py`) and manifest
|
||||
(`assets/models/blender/manifest.json`) keep the assets reproducible; the game selects variants
|
||||
by NPC role/outfit and keeps the procedural rig and primitive props as offline fallbacks. The
|
||||
existing AI tools remain the source of art direction for fabric textures, palettes, and the
|
||||
screen-design compare/judge gate.
|
||||
|
||||
The library targets 8 character variants (casual, jacket, coat, vest, dress, hoodie, firefighter,
|
||||
guard), 18 gear pieces, and 20 props — including replacements for the current generated street
|
||||
furniture, lamps, trees, doors, marquee, and a venue car. Integration must preserve the
|
||||
`Rig/Head` focus anchor, seed-driven outfit determinism, collider/navigation footprints, and the
|
||||
frame budget measured by `smoke_npc_budget.gd`. See
|
||||
[`docs/plan-graphics-v0.6.md`](docs/plan-graphics-v0.6.md) and
|
||||
[ADR-0013](docs/adr/0013-blender-authored-npc-characters.md).
|
||||
|
||||
The scenario should test the player’s ability to distinguish ordinary Hollywood Boulevard activity from a genuine security problem. The best solution is usually a simple gesture, clear direction, or credential check rather than immediate escalation.
|
||||
|
||||
Future venues can reuse this structure while changing the rules, equipment, staff roles, and types of access decisions.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Step Line
|
||||
# Gig Line
|
||||
|
||||
Step Line is a workplace security simulation about observation, communication, access verification, and proportionate escalation. The player works security at public-facing venues around Hollywood Boulevard, managing stairs, entrances, crowds, guests, staff, and occasional serious threats.
|
||||
Gig Line is a workplace security simulation about observation, communication, access verification, and proportionate escalation. The player works security at public-facing venues around Hollywood Boulevard, managing stairs, entrances, crowds, guests, staff, and occasional serious threats.
|
||||
|
||||
The central design goal is simple:
|
||||
|
||||
@@ -23,19 +23,19 @@ The central design goal is simple:
|
||||
|
||||
| Platform | Package |
|
||||
| --- | --- |
|
||||
| **Windows — installer** | [`step-line-setup-<ver>.exe`](https://git.allucanget.biz/zwitschi/security-game/releases) · contains all game data, shortcuts + uninstaller |
|
||||
| **Windows — portable** | [`step-line-windows-<ver>.exe`](https://git.allucanget.biz/zwitschi/security-game/releases) · 64-bit, no install needed |
|
||||
| **Linux — AppImage** | [`step-line-<ver>-x86_64.AppImage`](https://git.allucanget.biz/zwitschi/security-game/releases) · single file, `chmod +x` and run |
|
||||
| **Linux — portable** | [`step-line-linux-<ver>.x86_64`](https://git.allucanget.biz/zwitschi/security-game/releases) · 64-bit, `chmod +x` and run |
|
||||
| **macOS** | [`step-line-macos-<ver>.zip`](https://git.allucanget.biz/zwitschi/security-game/releases) · unsigned build, quarantine note below |
|
||||
| **Web (browser)** | [`step-line-web-<ver>.zip`](https://git.allucanget.biz/zwitschi/security-game/releases) · unzip and serve the folder from any static host (no special headers) |
|
||||
| **Windows — installer** | [`gig-line-setup-<ver>.exe`](https://git.allucanget.biz/zwitschi/security-game/releases) · contains all game data, shortcuts + uninstaller |
|
||||
| **Windows — portable** | [`gig-line-windows-<ver>.exe`](https://git.allucanget.biz/zwitschi/security-game/releases) · 64-bit, no install needed |
|
||||
| **Linux — AppImage** | [`gig-line-<ver>-x86_64.AppImage`](https://git.allucanget.biz/zwitschi/security-game/releases) · single file, `chmod +x` and run |
|
||||
| **Linux — portable** | [`gig-line-linux-<ver>.x86_64`](https://git.allucanget.biz/zwitschi/security-game/releases) · 64-bit, `chmod +x` and run |
|
||||
| **macOS** | [`gig-line-macos-<ver>.zip`](https://git.allucanget.biz/zwitschi/security-game/releases) · unsigned build, quarantine note below |
|
||||
| **Web (browser)** | [`gig-line-web-<ver>.zip`](https://git.allucanget.biz/zwitschi/security-game/releases) · unzip and serve the folder from any static host (no special headers) |
|
||||
|
||||
**All releases:** <https://git.allucanget.biz/zwitschi/security-game/releases>
|
||||
|
||||
> Downloads are public (no sign-in required) since the host's instance-wide
|
||||
> `REQUIRE_SIGNIN_VIEW` was disabled on 2026-10-31.
|
||||
>
|
||||
> **Latest: v0.5.0** — articulated NPC characters with LOD and idle behaviour, plus movement feel, gamepad support, and key remapping.
|
||||
> **Latest: v0.6.0** — Blender-authored character/gear/prop library with baked AO, higher-resolution environment textures, and phase-driven library props.
|
||||
> Releases are built automatically when a `vX.Y.Z` tag is pushed
|
||||
> (see [`docs/RELEASING.md`](docs/RELEASING.md)); manual builds use
|
||||
> `./tools/build_releases.sh`. Installer/AppImage packaging runs in the same pipeline
|
||||
@@ -45,7 +45,7 @@ The central design goal is simple:
|
||||
> the page and tag URLs are public.
|
||||
>
|
||||
> macOS builds are unsigned: right-click → Open, or clear the quarantine with
|
||||
> `xattr -dr com.apple.quarantine step-line-macos.zip`. Build instructions and
|
||||
> `xattr -dr com.apple.quarantine gig-line-macos.zip`. Build instructions and
|
||||
> the release pipeline live in [`docs/RELEASING.md`](docs/RELEASING.md).
|
||||
|
||||
If you are here to develop or test locally instead of downloading, jump to
|
||||
@@ -144,7 +144,7 @@ The architecture maps onto Godot 4 nodes, scenes, and custom resources as descri
|
||||
- **NPC behavior:** Custom lightweight finite state machines driven by `NpcProfile` resources
|
||||
- **UI and audio:** Godot `Control` nodes, themes, audio buses, and `AudioStreamPlayer` nodes
|
||||
- **Art assets:** authored PNGs and the in-game generated set in `assets/textures/ai/game_ready/`; street-furniture models in `assets/models/generated/` build from `tools/model_pipeline.py`; structural geometry stays procedural (`WorldBuilder`). The legacy `tools/generate_textures.py --ai` batch remains available.
|
||||
- **Asset tooling:** `tools/asset_pipeline.py` generates role-aware assets through OpenRouter and provides crop, resize, seamless-tile, and image-audit commands, including gameplay screenshot checks. `tools/model_pipeline.py` builds and audits the prop `.glb` models.
|
||||
- **Asset tooling:** `tools/asset_pipeline.py` generates role-aware assets through OpenRouter (`--variants N` produces alternative images) and provides crop, resize, seamless-tile, and image-audit commands, including gameplay screenshot checks. `tools/model_pipeline.py` builds and audits the street-prop `.glb` models; `tools/npc_pipeline.py` builds and audits the skinned NPC base model.
|
||||
- **Testing:** GUT (Godot Unit Test) in headless mode
|
||||
- **Source control:** git on a Gitea host with Gitea Actions (`.gitea/workflows/build.yml`) for headless tests and desktop release builds
|
||||
- **Targets:** Windows, Linux, and macOS desktop first; web later
|
||||
@@ -221,6 +221,89 @@ Surface materials share one definition in `src/game/materials.gd`: named
|
||||
roughness/metallic profiles per surface, plus the environment tune-up (ACES
|
||||
tonemapping, glow, fog, colour grading) that the phase lighting applies.
|
||||
|
||||
### NPC character model
|
||||
|
||||
NPCs are moving from the procedural capsule rig to a skinned, UV-mapped base
|
||||
character authored in Blender ([ADR-0013](docs/adr/0013-blender-authored-npc-characters.md)).
|
||||
`tools/npc_pipeline.py` builds `assets/models/generated/npc_base.glb` plus the
|
||||
per-slot material maps. `NpcAvatar` instantiates that skinned model, tints its
|
||||
five material slots from the seed-driven outfit, and drives the bones from the
|
||||
existing animation code; the procedural rig remains the offline fallback (see
|
||||
the [graphics plan](docs/plan-graphics-v0.6.md)).
|
||||
|
||||
```bash
|
||||
# Print the frozen contract (slots, bone names, triangle budget)
|
||||
python3 tools/npc_pipeline.py list
|
||||
|
||||
# Rebuild the skinned base mesh + neutral maps in Blender and validate it
|
||||
flatpak run org.blender.Blender --background --python "$PWD/tools/npc_pipeline.py" -- build
|
||||
python3 tools/npc_pipeline.py audit
|
||||
|
||||
# Layer the AI clothing albedos (shirt/trousers) over the procedural weave, and
|
||||
# derive per-fabric outfit colours for the avatar palette
|
||||
python3 tools/npc_pipeline.py prepare
|
||||
python3 tools/npc_pipeline.py palette
|
||||
```
|
||||
|
||||
`prepare` resizes the AI fabric tiles (kept locally in `assets/textures/npc/ai/`,
|
||||
which is gitignored) onto the `cloth`/`pants` albedos; `build` bakes them into the
|
||||
`.glb` and regenerates the procedural normal + packed ORM maps. `palette` writes
|
||||
`assets/textures/npc/palette.json` using `model_pipeline.dominant_colors`.
|
||||
|
||||
### Blender asset library (loaded by the game)
|
||||
|
||||
`tools/blender_assets.py` models a larger library procedurally in Blender for the
|
||||
v0.6.0 integration: **8 skinned NPC characters** (casual, jacket, coat, hi-vis vest,
|
||||
dress, hoodie, firefighter, guard), each a drop-in for `npc_base.glb` (same bones,
|
||||
joint positions and tint slots); **18 gear pieces** authored at head/hand/back/chest
|
||||
attach points (caps, hood, helmet, backpack, SCBA tank, lanyard, phone, cup,
|
||||
clipboard, umbrella, camera, sign, flashlight, wand, radio, …); and **20 props**
|
||||
(octagonal bollard and bin, slatted bench, hydrant, news box, planters, street lamp,
|
||||
street and palm trees, A-frame sign, wall lantern, cone, crowd barrier, stanchion,
|
||||
handrail, podium, double door, marquee, sedan).
|
||||
|
||||
`NpcAvatar` selects a character by role/seed, tints the five contract slots, and
|
||||
attaches gear at the documented points; `WorldBuilder` loads the library props
|
||||
with colliders/avoidance and drives their emissive parts from the day/night
|
||||
cycle. The single `npc_base.glb` and primitive props remain offline fallbacks.
|
||||
Contract and integration notes: [`docs/blender-assets.md`](docs/blender-assets.md).
|
||||
|
||||
| Characters | Props |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|
||||
```bash
|
||||
flatpak run org.blender.Blender --background --python "$PWD/tools/blender_assets.py" -- build
|
||||
flatpak run org.blender.Blender --background --python "$PWD/tools/blender_assets.py" -- render
|
||||
python3 tools/blender_assets.py sheet # docs/blender_previews/sheet_*.jpg
|
||||
python3 tools/blender_assets.py audit # no Blender needed
|
||||
godot --headless --path . --import
|
||||
```
|
||||
|
||||
### Confirmed game assets
|
||||
|
||||
Everything below is audited and shipped in the game; development-only outputs
|
||||
(AI concept sheets, Blender sources, raw AI fabrics) are gitignored.
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
- **Surface textures:** the six tiles above, loaded by `src/game/materials.gd` and `WorldBuilder`.
|
||||
- **UI art:** `assets/textures/ai/game_ready/icons/` (flashlight, door lock, wand, REC) and `assets/textures/ai/game_ready/cards/` (ID, wristband, ticket, credential, guest list), loaded by `src/game/ui/hud.gd`.
|
||||
- **Generated models:** `assets/models/generated/*.glb` — bollard, bench, trash bin, planter, newspaper box, hydrant, and the NPC base — plus their colour atlases.
|
||||
- **NPC material maps:** `assets/textures/npc/*_{albedo,normal,orm}.png` and `palette.json`.
|
||||
|
||||
Audit them all:
|
||||
|
||||
```bash
|
||||
python3 tools/asset_pipeline.py audit assets/textures/ai/game_ready
|
||||
python3 tools/model_pipeline.py audit
|
||||
python3 tools/npc_pipeline.py audit
|
||||
```
|
||||
|
||||
### Screen design references
|
||||
|
||||
Screen mockups and screenshot comparison use `tools/screen_designs.py`. It turns the
|
||||
@@ -391,6 +474,8 @@ The full arc42-style architecture documentation lives in [`docs/architecture.md`
|
||||
- [`MEMORY.md`](MEMORY.md) — Persistent session notes.
|
||||
- [`docs/architecture.md`](docs/architecture.md) — arc42-style architecture documentation.
|
||||
- [`docs/adr/`](docs/adr/) — Architecture Decision Records.
|
||||
- [`docs/plan-graphics-v0.6.md`](docs/plan-graphics-v0.6.md) — Graphics-quality plan: Blender-authored NPC characters.
|
||||
- [`docs/blender-assets.md`](docs/blender-assets.md) — Blender asset library: characters, gear, props, attach points.
|
||||
|
||||
## Development Status
|
||||
|
||||
@@ -427,10 +512,12 @@ Implemented so far:
|
||||
- Prop collision and placement (v0.4.0): every prop fits a static collider from its mesh bounds and publishes a navigation obstacle, so the guard and guests cannot walk through benches, bins, or bollards; placements are grouped into zones with a validator, and deterministic variants add rotation, scale, and small clutter
|
||||
- Prop atlas (v0.3.1): `tools/model_pipeline.py` packs each prop's palette into one power-of-two colour atlas plus a metallic/roughness atlas and emits a single material with per-vertex UVs, so a prop is one draw material rather than one per part; `audit` enforces the contract
|
||||
- Generated prop models (v0.3.0 work): `tools/model_pipeline.py` builds bollards, benches, bins, planters, newspaper boxes and a hydrant as `.glb` props with per-part materials, loaded by `WorldBuilder` with a procedural fallback; the optional AI concept step (`model_pipeline.py concept`) supplies per-prop colour palettes
|
||||
- Web build: `build/step-line-web-<version>.zip` (Godot's non-threaded web template, no COOP/COEP headers needed) built by `tools/build_releases.sh` and published with the desktop artifacts; serve the unzipped folder from any static host
|
||||
- Environment pass (v0.6.0): 1024² brick/concrete/asphalt with derived tangent normal maps, 256² baked AO maps for every Blender character/gear/prop, and library props whose bulbs/LEDs/sign faces follow the day/night phase
|
||||
- NPC characters + library (v0.6.0): `NpcAvatar` loads one of 8 Blender-authored skinned variants (role/seed selected) with five tintable material slots, `Rig/Head` focus anchor, and attach-point gear; `npc_base.glb` and the procedural rig remain fallbacks. `tools/blender_assets.py` builds/audits the character, gear, and 20-prop library; `WorldBuilder` loads the replacements and new street dressing (lamp, trees, palms, planters, barriers, podium, doors, marquee, sedan, handrail) with colliders, navigation obstacles, and phase-driven emissives
|
||||
- Web build: `build/gig-line-web-<version>.zip` (Godot's non-threaded web template, no COOP/COEP headers needed) built by `tools/build_releases.sh` and published with the desktop artifacts; serve the unzipped folder from any static host
|
||||
- Aim-free selection pass (v0.3.0 work, [ADR-0011](docs/adr/0011-aim-free-selection-and-precision-walk.md)): the focus cone ranks candidates by angle then distance, `Tab` and the mouse wheel cycle nearby people nearest-first and pin the selection, right click clears focus with a short re-acquire grace, the HUD reports how many people are in the ring, and holding `Shift` walks at half speed for close work
|
||||
|
||||
Not yet implemented: live 3D body-camera replay, editor-based GUT integration, and crowd performance profiling/LOD (see `TODO.md`).
|
||||
Not yet implemented: a desktop-only Forward+ preset (the renderer stays GL Compatibility for Web parity), live 3D body-camera replay, editor-based GUT integration, and crowd performance profiling/LOD (see `TODO.md`).
|
||||
|
||||
### Smoke Test Checklist
|
||||
|
||||
@@ -440,7 +527,7 @@ Not yet implemented: live 3D body-camera replay, editor-based GUT integration, a
|
||||
4. The guard's camera sits at eye height and the guard stands on the landing, not inside the building.
|
||||
5. Walking into the steps carries the guard up onto the landing (steps have colliders).
|
||||
6. The skyline backdrop shows silhouettes against the sky, not an opaque black slab.
|
||||
7. The `STEP LINE` marquee is readable from the sidewalk.
|
||||
7. The `GIG LINE` marquee is readable from the sidewalk.
|
||||
8. NPCs spawn and approach the entrance during the first minutes.
|
||||
9. Looking at an NPC inside the focus cone shows a blue focus circle in front of the face, the person's name, and the HUD target line.
|
||||
10. `Space` or left click opens the action wheel; `A`/`D` highlight a slice, `Space` confirms it, `Esc`/right click cancels without a log entry.
|
||||
|
||||
@@ -1,11 +1,228 @@
|
||||
# TODO
|
||||
|
||||
Godot 4.3 prototype. Latest release **v0.5.0** (NPC characters and player movement). Headless
|
||||
suite **208/208** plus **56** Python tooling tests; the session, PLAN-verb, crowd, emergency,
|
||||
suite **222/222** plus **68** Python tooling tests; the session, PLAN-verb, crowd, emergency,
|
||||
timeline, audio, scenario-content, props, NPC-budget, movement, and windowed focus/radial smokes
|
||||
pass. Anonymous downloads are enabled on the host.
|
||||
|
||||
The scheduled presentation pass is complete (see `DONE.md`). Only optional future ideas remain.
|
||||
The scheduled presentation pass is complete (see `DONE.md`). The next scheduled pass is the
|
||||
graphics-quality work in [`docs/plan-graphics-v0.6.md`](docs/plan-graphics-v0.6.md).
|
||||
|
||||
> **Blender asset library integrated (2026-11-01):** 8 NPC character variants, 18 gear pieces,
|
||||
> and 20 street/venue props are now loaded by `NpcAvatar` and `WorldBuilder` from
|
||||
> `assets/models/blender/` (manifest as source of truth, see
|
||||
> [`docs/blender-assets.md`](docs/blender-assets.md)). The procedural rig and primitive props
|
||||
> remain offline fallbacks; AO/ORM baking is the remaining Phase 4 follow-up.
|
||||
|
||||
## Graphics quality — Blender-authored NPC characters (v0.6.0)
|
||||
|
||||
Detailed plan: [`docs/plan-graphics-v0.6.md`](docs/plan-graphics-v0.6.md) · [ADR-0013](docs/adr/0013-blender-authored-npc-characters.md)
|
||||
|
||||
### Phase 0 — Toolchain spike and contract freeze
|
||||
|
||||
- [x] Verify `flatpak run org.blender.Blender --version` reports Blender 5.2.x and runs headless in this shell.
|
||||
- [x] Run a minimal headless Blender export using an absolute `--python` path (sandbox cwd is `/app/blender`); confirm the output file lands where expected.
|
||||
- [x] Record the exact working invocation (flatpak + absolute paths) in `MEMORY.md`.
|
||||
- [x] Freeze the NPC asset contract in `docs/plan-graphics-v0.6.md` and ADR-0013: file paths, bone names, material slots, triangle budget.
|
||||
- [x] Fix the material-slot set (`skin` / `cloth` / `pants` / `accessory` / `hair`) and the ORM packing (R=occlusion, G=roughness, B=metallic).
|
||||
- [x] Choose and record the initial tier-0 triangle/vertex budget (≤ ~6000 tris / ~4000 verts) in the plan.
|
||||
- [x] Spike: Godot 4.3 imports the `.glb` as a `PackedScene` with a `Skeleton3D`; no clips are needed because `_animate()` drives the bones at runtime.
|
||||
|
||||
### Phase 1 — Blender authoring + export pipeline
|
||||
|
||||
- [x] Author the low-poly base humanoid in Blender: torso, articulated arms/legs, head with a face front, hands, shoes.
|
||||
- [x] Rig with an armature whose bones mirror the node contract (`Head`, `HipL/R`, `KneeL/R`, `ShoulderL/R`, `ElbowL/R`, `HandL/R`); origin at the feet, +Y up.
|
||||
- [x] UV-unwrap the mesh into a power-of-two layout, one region per material slot.
|
||||
- [x] Save the editable source as `assets/models/npc/npc_base.blend` (committed).
|
||||
- [x] Add `tools/npc_pipeline.py` with `list` / `build` / `audit` subcommands, run through the Blender flatpak.
|
||||
- [x] `build` exports `assets/models/generated/npc_base.glb` plus the material textures.
|
||||
- [x] Bake neutral albedo per slot, normal maps (fabric weave, denim, knit), and the packed ORM map.
|
||||
- [x] Bake ambient occlusion into the albedo or the ORM R channel (the Compatibility renderer has no SSAO).
|
||||
- [x] `audit` enforces UVs in [0,1], expected bone names, triangle/vertex budget, feet at y≈0, and material-slot presence.
|
||||
- [x] Reuse the `model_pipeline.py` colour-space discipline (linear factors vs sRGB textures) in the exporter.
|
||||
- [x] Run `godot --headless --path . --import` and confirm `npc_base.glb` imports without errors.
|
||||
|
||||
### Phase 2 — AI-assisted texturing
|
||||
|
||||
- [x] Generate seamless clothing albedos (shirt, trousers, jacket, hi-vis vest, coat, dress) with `tools/asset_pipeline.py generate` (OpenRouter, `openai/gpt-5-image-mini`) — written to `assets/textures/npc/ai/` (512×512).
|
||||
- [x] Tile-edge blending on each fabric — applied automatically by `generate --role tile` (`make_seamless`); the `tile` audit's edge check confirms continuity.
|
||||
- [x] Run `asset_pipeline.py audit` on the outputs — 6 good, 0 bad (role `tile`).
|
||||
- [x] Commit the validated textures under `assets/textures/npc/ai/` (tracked in the v0.6.0 asset commit).
|
||||
- [x] Derive per-role outfit palettes via `model_pipeline.dominant_colors` — written to `assets/textures/npc/palette.json` (`npc_pipeline.py palette`).
|
||||
- [x] Bake normal/roughness procedurally and layer the AI albedos on top — `npc_pipeline.py prepare` + Blender `build` keeps the AI albedo for `cloth`/`pants` and regenerates normal/ORM; GLB audit ok (1204 tris / 2488 verts).
|
||||
- [x] Regenerate the staged captures and run `screen_designs.py judge` after the Phase 3 integration (11 good / 0 bad in `compare`; judge: no diff).
|
||||
|
||||
### Phase 3 — Godot integration
|
||||
|
||||
- [x] Add `NpcAvatar._load_model()` that instantiates the imported `npc_base.glb` `PackedScene`.
|
||||
- [x] Keep `_build_body()` as the offline fallback; choose per-avatar and record `loaded_model` / `fallback_avatar` for tests.
|
||||
- [x] Map `outfit_for()` onto the model's material slots so seed variety, `layer_for`, `hand_item_for`, and archetype colors survive.
|
||||
- [x] Keep accessory attachments (cap / hood / backpack / sunglasses / lanyard) under `Rig` / `Head`.
|
||||
- [x] Add `SurfaceMaterials` baked-map recipes (`npc()` / `skin()` / `cloth()`); stop calling `Materials.flat()` for the body.
|
||||
- [x] Add `_set_limb_rotation()` so `_animate()` / `_idle_layer()` write to bones or pivots; keep `Rig/Head` findable by the focus cone.
|
||||
- [x] Update `test_npc_character`, `test_npc_theater`, and `test_target_cycle` for the skinned path (or document why they stay on the fallback).
|
||||
- [x] Re-measure `smoke_npc_budget.gd`; tune `LOD_TIERS` if the skinned rig is heavier up close.
|
||||
- [x] Re-verify the focus-cone smoke (`smoke_focus_radial.gd`) and the movement smoke.
|
||||
|
||||
### Phase 4 — Environment and lighting lift ✅
|
||||
|
||||
- [x] Bake AO for the prop set through the Blender library (replacements above) or into the ORM/albedo maps behind the existing `WorldBuilder` contract; keep the procedural fallback.
|
||||
- [x] Regenerate higher-resolution brick / concrete / asphalt textures via `asset_pipeline.py` with normal maps, keeping the current UV scales; apply the same map discipline to the library's street materials.
|
||||
- [x] Evaluate a desktop-only Forward+ export preset (SSAO/SSIL/volumetrics); keep the Web preset on GL Compatibility with `thread_support=false`.
|
||||
- [x] Record the renderer decision in an ADR addendum or `MEMORY.md`.
|
||||
- [x] Regenerate `docs/screenshots/` with `capture_screenshots.gd` and run `screen_designs.py compare`.
|
||||
|
||||
### Phase 5 — Verification and release
|
||||
|
||||
- [x] Headless suite green: fallback build, outfit determinism, `Rig/Head` presence, material recipe assertions, `npc_pipeline.py audit`, and `blender_assets.py audit` + manifest tests (skip Blender builds with a reason when Blender is absent).
|
||||
- [x] Windowed smokes pass: `smoke_npc_budget.gd`, `smoke_movement.gd`, `smoke_focus_radial.gd`, `smoke_props.gd`.
|
||||
- [x] `screen_designs.py compare` and `judge` show no regressions; `blender_assets.py sheet` contact sheets reviewed for the character/gear/prop library.
|
||||
- [ ] Bump `config/version` to `0.6.0`.
|
||||
- [ ] Update `README.md`, `docs/architecture.md` (Level 2 gains `tools/npc_pipeline.py`), and mark ADR-0013 Accepted.
|
||||
- [ ] Publish the release per `docs/RELEASING.md`.
|
||||
|
||||
## Visual alignment with rendered mockups
|
||||
|
||||
The AI-rendered references in `docs/screens/designs/` (start menu, pause, settings,
|
||||
shift review, radial menu, HUD, scene views) describe the intended look of every
|
||||
screen. This pass reorganizes how generated images are stored, makes the game load
|
||||
the proposed interface/scene art from disk, and aligns the selection screens, HUD,
|
||||
and scene visuals with those mockups — verified with
|
||||
`tools/screen_designs.py compare` + `judge`.
|
||||
|
||||
### Storage restructure
|
||||
|
||||
- [ ] Decide the canonical runtime image root (e.g. `assets/images/` for UI/scene art) and record it in `MEMORY.md` and an ADR.
|
||||
- [ ] Move the reference mockups from `docs/screens/designs/` into the runtime root (keep `docs/screens/designs/` as a docs-only copy or replace it with the new path).
|
||||
- [ ] Document one convention for game vs reference vs development images across `assets/textures/`, `assets/images/`, `assets/models/generated/`, `docs/screenshots/`, and `docs/screens/designs/`.
|
||||
- [ ] Re-run `godot --headless --path . --import` to regenerate `.import` sidecars after any move.
|
||||
- [ ] Confirm `.gitignore` still excludes development-only outputs (concepts, Blender sources, raw AI fabrics) under the new layout.
|
||||
|
||||
### Documentation path updates
|
||||
|
||||
- [ ] Update `README.md` paths (screen-designs section, confirmed-assets links, the `assets/images/backgrounds` resize example).
|
||||
- [ ] Update `docs/architecture.md`, `PLAN.md`, and ADR-0010/0011/0012/0013 references to `docs/screens/designs/`.
|
||||
- [ ] Update `tools/screen_designs.py` docstrings and mark the old paths in `DONE.md` as historical.
|
||||
|
||||
### Code path updates
|
||||
|
||||
- [ ] Point `tools/screen_designs.py generate` at the new design output path and keep `compare`/`judge` reading the right locations.
|
||||
- [ ] Update GDScript `preload()` paths (`materials.gd`, `hud.gd`, `world_builder.gd`) if the game textures move.
|
||||
- [ ] Update `capture_screenshots.gd` / `capture_prop_gallery.gd` output directories if captures relocate.
|
||||
|
||||
### Load proposed interfaces + scene art from disk
|
||||
|
||||
- [ ] Add a central UI/scene art loader (e.g. `src/game/ui/art.gd`) that loads the mockup-derived images from the new runtime root, with a procedural fallback when a file is missing.
|
||||
- [ ] Wire the start menu and venue selection screen to use the `start_menu` scene art.
|
||||
- [ ] Wire the pause overlay, settings, shift review, radial menu, and HUD chrome to their reference images.
|
||||
|
||||
### Align in-game visuals
|
||||
|
||||
- [ ] Make the venue selection screen match the `start_menu` mockup (layout, palette, background).
|
||||
- [ ] Make the HUD match the `targeting_npc` / `escalation_hud` mockups (focus ring, escalation meter, log, hints).
|
||||
- [ ] Align the remaining panels (pause, settings, shift review, radio, wand, timeline) to their mockups.
|
||||
- [ ] Align scene lighting, fog, and post-processing for `venue_overview` and `late_night_avenue`.
|
||||
|
||||
### Verify against the mockups
|
||||
|
||||
- [ ] Regenerate the staged captures and run `screen_designs.py compare` (no gross drift).
|
||||
- [ ] Run `screen_designs.py judge` and clear every `diff` verdict (target `match`/`review`).
|
||||
- [ ] Add the capture + compare step to CI or a repeatable smoke so drift is caught early.
|
||||
|
||||
## Improve in-game pop-ups
|
||||
|
||||
- [ ] Improve the visual appearance of in-game pop-ups (layout, button style, palette, icons, and text styling).
|
||||
|
||||
## Realistic backgrounds, textures and scene references
|
||||
|
||||
Goal: replace the outdated flat skyline backdrop (`assets/textures/skyline.png`, black boxes
|
||||
with dot windows) and the flat-looking surface tiles with AI-generated, more realistic
|
||||
backgrounds and textures. Add a larger set of example 3D scene images as art direction, and
|
||||
measure the in-game result against that target with the screenshot tools. Plan:
|
||||
[`PLAN.md` → Visual Quality Direction → Realism pass](PLAN.md#realism-pass).
|
||||
|
||||
**Rule for every step: never overwrite an existing image.** New outputs get new paths. When a
|
||||
path is taken, the tools pick the next free `-vN` suffix. Raw AI output stays on disk under
|
||||
`assets/textures/ai/v2/raw/` (gitignored like the other raw AI folders). Processed game-ready
|
||||
candidates go to `assets/textures/ai/game_ready/v2/`, scene references to `docs/screens/concepts/`,
|
||||
and trial captures and reports to `docs/screens/realism/<run>/`.
|
||||
|
||||
### Step 1 — Tooling (`tools/scene_concepts.py`)
|
||||
|
||||
- [ ] Add a catalogue of prompts in three groups: `scene` (16:9 example 3D game frames),
|
||||
`backdrop` (4:1 skyline panoramas with transparent sky), and `texture` (seamless PBR
|
||||
surface tiles). Each entry has a name, prompt, role and target path.
|
||||
- [ ] `generate [--group] [--only] [--variants N]`: call the OpenRouter image API through
|
||||
`generate_textures.generate_image` (key from the repo `.env`), save raw output, and never
|
||||
overwrite (`next_free_path`). Print every file written.
|
||||
- [ ] `process`: turn raw backdrops into 2048×512 RGBA strips that tile horizontally (edge
|
||||
crossfade, sky made fully transparent, hard alpha for the alpha-scissor material). Turn raw
|
||||
textures into seamless 1024² tiles (`asset_pipeline.make_seamless`) plus a normal map
|
||||
(`make_normal_maps.normal_map`). Skip outputs that already exist.
|
||||
- [ ] `audit`: run `asset_pipeline.audit_image` on the processed candidates (`backdrop` /
|
||||
`tile` roles) and fail on any bad result.
|
||||
- [ ] `realism`: for each trial capture, report the Pillow detail metrics (edge density,
|
||||
contrast, colour spread, saturation) next to the matching concept image. Optionally ask a
|
||||
vision model (`google/gemini-2.5-flash-lite`) for 0–10 realism scores (lighting,
|
||||
materials, texture detail, backdrop, overall) with concrete fixes. Write a new JSON +
|
||||
Markdown report per run.
|
||||
- [ ] Unit tests (`tools/test_scene_concepts.py`): catalogue shape, the no-overwrite path
|
||||
picker, backdrop/tile processing on synthetic images, metrics, and the judge payload/parse
|
||||
(offline, no network).
|
||||
|
||||
### Step 2 — Example 3D scene images
|
||||
|
||||
- [ ] Generate 3D-game-style example frames for every venue and time of day: Hollywood entrance
|
||||
(morning, golden hour, rainy night), boulevard skyline view (day, dusk), Joke Shop alley
|
||||
(evening, close-up of the door), studio stage door (night arrival), sidewalk material
|
||||
close-up, entrance steps and podium, crowd-control barrier line, and a responder arrival
|
||||
wide shot.
|
||||
- [ ] Review the set and record the visual targets each frame sets (sky, haze, material detail,
|
||||
backdrop depth) in the report.
|
||||
|
||||
### Step 3 — Backgrounds (replace the outdated skyline)
|
||||
|
||||
- [ ] Generate skyline panoramas for day, dusk and night (lit windows): Hollywood/Los Angeles
|
||||
mid-rise blocks, palm silhouettes, the hills on the horizon, a transparent sky, and a
|
||||
straight-on view, in several variants each.
|
||||
- [ ] Process them into `assets/textures/ai/game_ready/v2/skyline_{day,dusk,night}.png`,
|
||||
seamless horizontally for the 3× UV repeat around the backdrop ring.
|
||||
|
||||
### Step 4 — Textures
|
||||
|
||||
- [ ] Generate higher-detail seamless tiles: red brick facade, granite/limestone facade
|
||||
(studio), sidewalk concrete pavers with joints, asphalt with aggregate, wet night asphalt,
|
||||
stone steps, striped awning canvas, and stucco wall.
|
||||
- [ ] Process them into 1024² game-ready tiles plus normal maps in
|
||||
`assets/textures/ai/game_ready/v2/` and audit them.
|
||||
|
||||
### Step 5 — In-game trial captures (no game-code change)
|
||||
|
||||
- [ ] Add `src/test/capture_realism.gd`. It boots the Hollywood venue, captures a fixed shot
|
||||
list (entrance day, boulevard/skyline day, boulevard night), and can swap textures at
|
||||
runtime from `--skyline=` / `--brick=` / `--concrete=` / `--asphalt=` user arguments. The
|
||||
swap finds materials by their current texture, so baseline and candidate frames use the
|
||||
same camera. Writes to `user://screenshots/realism/<tag>/`.
|
||||
- [ ] Capture the baseline (current assets) and the candidate set (v2 skyline + textures), and
|
||||
copy them into `docs/screens/realism/<run>/{baseline,candidate}/`.
|
||||
|
||||
### Step 6 — Compare against the realistic target
|
||||
|
||||
- [ ] Run `scene_concepts.py realism` on baseline and candidate. Record the metric deltas and the
|
||||
vision scores, and note which candidate textures/backdrops win.
|
||||
- [ ] Note the remaining realism gaps (sky/fog, lighting, building geometry, NPC fidelity) as
|
||||
follow-up tasks below.
|
||||
|
||||
### Step 7 — Adopt the winners (after the in-flight normal-map/AO work is committed)
|
||||
|
||||
- [ ] Point `WorldBuilder.T_SKYLINE` at the chosen v2 skyline (separate day/night textures through
|
||||
`set_night_mode`), and retune `albedo_color` for the realistic strip.
|
||||
- [ ] Switch `materials.gd` / `world_builder.gd` surface preloads to the chosen v2 tiles + normal
|
||||
maps, keeping the current UV scales. Update `test_materials.gd` / `test_world.gd`.
|
||||
- [ ] Regenerate `docs/screenshots/` with `capture_screenshots.gd`, then run
|
||||
`screen_designs.py compare` + `judge` and `scene_concepts.py realism`. There must be no
|
||||
`diff` verdict regressions, and realism scores must not drop.
|
||||
- [ ] Update `README.md` (tool usage + gallery), `MEMORY.md` and `docs/architecture.md`.
|
||||
|
||||
## Future ideas (optional, not scheduled)
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 30 KiB |