Refit the tourist backpack and photo prop
Hide the tourist photo-pose bag outside camera poses, flatten the backpack straps onto the chest, rebuild the gear GLB/AO and publish the before/after NPC sheet.
This commit is contained in:
1 parent
e7ee177c31
commit
7c1a4bfd69
14 files changed
+108
-45
No files matched your search
@@ -4,6 +4,15 @@ All notable user-facing changes to Gig Line. The format follows
|
|||||||
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions use
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions use
|
||||||
Semantic Versioning.
|
Semantic Versioning.
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Tourists no longer appear to carry two backpacks: the photo-pose camera bag is
|
||||||
|
hidden except while they are actually taking a picture.
|
||||||
|
- Backpack shoulder straps are now flattened bands that sit outside the chest
|
||||||
|
instead of thin rods partly buried in the torso.
|
||||||
|
|
||||||
## [0.9.3] — 2026-11-02
|
## [0.9.3] — 2026-11-02
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -66,8 +66,10 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
|
|||||||
crowd). It uses a fixed orbit camera, mid-grey studio backdrop, key/fill lights, no HUD/crowd/animation;
|
crowd). It uses a fixed orbit camera, mid-grey studio backdrop, key/fill lights, no HUD/crowd/animation;
|
||||||
raw frames stay in `tmp/`, and `tools/npc_baseline.py sheet` builds the committed
|
raw frames stay in `tmp/`, and `tools/npc_baseline.py sheet` builds the committed
|
||||||
`sheet_npc_baseline.jpg`. Criteria and measurements are in `docs/npc-visual-review.md`. The first
|
`sheet_npc_baseline.jpg`. Criteria and measurements are in `docs/npc-visual-review.md`. The first
|
||||||
ranked gaps are: tourist photo prop duplicates the backpack silhouette; shallow/faceted face;
|
ranked gaps were: tourist photo prop duplicates the backpack silhouette; shallow/faceted face;
|
||||||
backpack straps read as rigid bars. No model/GLB changes were made in the baseline step.
|
backpack straps read as rigid bars. The tourist prop now hides outside photo/film poses and the
|
||||||
|
backpack straps were refit as flat forward bands (GLB + AO rebuilt); the face remains P1. Before/
|
||||||
|
after sheet: `sheet_npc_baseline_compare.jpg`.
|
||||||
- **UI flow smoke (2026-11-02):** `smoke_ui_flows.gd` boots the real scene and
|
- **UI flow smoke (2026-11-02):** `smoke_ui_flows.gd` boots the real scene and
|
||||||
exercises inspection allow/deny, normal vs emergency radio, Help filtering,
|
exercises inspection allow/deny, normal vs emergency radio, Help filtering,
|
||||||
wand allow/deny and review return-to-menu. Keep this smoke in the 0.9.6
|
wand allow/deny and review return-to-menu. Keep this smoke in the 0.9.6
|
||||||
@@ -741,7 +743,7 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
|
|||||||
- Venue `door_staff_lines` are logged through `GameManager` (inspect / wand / refuse). They are not a separate dialogue UI.
|
- Venue `door_staff_lines` are logged through `GameManager` (inspect / wand / refuse). They are not a separate dialogue UI.
|
||||||
- Access loadout is implemented; do not re-fix `override_access` as if it were still unread.
|
- Access loadout is implemented; do not re-fix `override_access` as if it were still unread.
|
||||||
- `Save.SETTINGS_FILE` is declared and unused; settings persistence goes through `Save.load_settings()` / `save_settings()`.
|
- `Save.SETTINGS_FILE` is declared and unused; settings persistence goes through `Save.load_settings()` / `save_settings()`.
|
||||||
- Headless suite is **261/261**; Python tooling is **99/99**; screenshot compare is **21 good / 0 bad** and the automatic judge is **21/21 match, 0 diff**. Remaining visual follow-ups are in `TODO.md`.
|
- Headless suite is **262/262**; Python tooling is **99/99**; screenshot compare is **21 good / 0 bad** and the automatic judge is **21/21 match, 0 diff**. Remaining visual follow-ups are in `TODO.md`.
|
||||||
- Typed spawn signal extra args: handlers must accept the 4th `event: Dictionary` or Godot will not call them (`test_shift_coordinator_runs_a_shift` failed until `_on_spawn` took four args).
|
- Typed spawn signal extra args: handlers must accept the 4th `event: Dictionary` or Godot will not call them (`test_shift_coordinator_runs_a_shift` failed until `_on_spawn` took four args).
|
||||||
- GDScript inference trap that broke the main scene: a `var` typed as a base class
|
- GDScript inference trap that broke the main scene: a `var` typed as a base class
|
||||||
(`radial: CanvasLayer`) exposes script members as `Variant`, so `var x := radial.is_open` is a
|
(`radial: CanvasLayer`) exposes script members as `Variant`, so `var x := radial.is_open` is a
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ see `AGENTS.md` for the workflow and `MEMORY.md` for the decisions behind these
|
|||||||
|
|
||||||
Baseline: **v0.9.3** (2026-11-02). Three playable venues, tutorial/campaign/checkpoint resume,
|
Baseline: **v0.9.3** (2026-11-02). Three playable venues, tutorial/campaign/checkpoint resume,
|
||||||
difficulty/help, audio/accessibility settings, a 24-NPC budget baseline, a complete
|
difficulty/help, audio/accessibility settings, a 24-NPC budget baseline, a complete
|
||||||
escalation/access/radio loop, shift review, the Blender asset library, 261 headless + 99 Python
|
escalation/access/radio loop, shift review, the Blender asset library, 262 headless + 99 Python
|
||||||
tests, a 21/21 clean screen-design judge and an optional Forward+ Desktop HQ build. This is a strong
|
tests, a 21/21 clean screen-design judge and an optional Forward+ Desktop HQ build. This is a strong
|
||||||
release-candidate foundation, not yet a 0.10.0 release candidate.
|
release-candidate foundation, not yet a 0.10.0 release candidate.
|
||||||
|
|
||||||
@@ -76,8 +76,9 @@ fallback behavior.
|
|||||||
the side-by-side after sheet without overwriting it.)
|
the side-by-side after sheet without overwriting it.)
|
||||||
|
|
||||||
Completed 2026-11-02: 48-frame neutral studio baseline, eight criteria, current fit and
|
Completed 2026-11-02: 48-frame neutral studio baseline, eight criteria, current fit and
|
||||||
triangle/vertex measurements, and ranked P1/P2 gaps are in `docs/npc-visual-review.md`. No
|
triangle/vertex measurements, and ranked P1/P2 gaps are in `docs/npc-visual-review.md`. The
|
||||||
character model was changed in this pass.
|
baseline itself changed no character models; the first P1 gear fixes are recorded under Gear
|
||||||
|
and equipment fit.
|
||||||
|
|
||||||
### Body proportions and silhouette
|
### Body proportions and silhouette
|
||||||
|
|
||||||
@@ -126,7 +127,11 @@ character model was changed in this pass.
|
|||||||
head-band, skirt/hip clearance and inner-face clearances.
|
head-band, skirt/hip clearance and inner-face clearances.
|
||||||
- [ ] Re-fit caps, beanie, hood, sunglasses and firefighter helmet against the refined face/hair;
|
- [ ] Re-fit caps, beanie, hood, sunglasses and firefighter helmet against the refined face/hair;
|
||||||
ensure eyebrow/ear clearance and a readable face with the hood up.
|
ensure eyebrow/ear clearance and a readable face with the hood up.
|
||||||
- [ ] Re-fit backpack, shoulder bag and SCBA tank against the revised torso/hips; fix strap and
|
- [x] Hide the tourist's photo-pose camera bag outside `photo`/`film` poses; it duplicated the
|
||||||
|
backpack silhouette. (`photo_prop_visible_for_pose()` plus regression test.)
|
||||||
|
- [x] Refit the backpack straps as flattened bands that follow the torso instead of rods partly
|
||||||
|
buried in the chest; rebuild the GLB, bake AO and confirm in the before/after sheet.
|
||||||
|
- [ ] Re-fit shoulder bag and SCBA tank against the revised torso/hips; fix remaining strap and
|
||||||
tank intersections and keep attachments outside the body silhouette.
|
tank intersections and keep attachments outside the body silhouette.
|
||||||
- [ ] Verify lanyards, radios, phones, cups, clipboards, umbrellas, cameras, protest signs,
|
- [ ] Verify lanyards, radios, phones, cups, clipboards, umbrellas, cameras, protest signs,
|
||||||
flashlights and wands sit correctly in the hand/grip and do not float.
|
flashlights and wands sit correctly in the hand/grip and do not float.
|
||||||
|
|||||||
@@ -29,8 +29,8 @@
|
|||||||
"bounds_godot": {
|
"bounds_godot": {
|
||||||
"max": [
|
"max": [
|
||||||
0.15,
|
0.15,
|
||||||
0.3,
|
0.297,
|
||||||
0.141
|
0.214
|
||||||
],
|
],
|
||||||
"min": [
|
"min": [
|
||||||
-0.15,
|
-0.15,
|
||||||
@@ -45,8 +45,8 @@
|
|||||||
"plastic_black"
|
"plastic_black"
|
||||||
],
|
],
|
||||||
"path": "assets/models/blender/npc_gear/backpack.glb",
|
"path": "assets/models/blender/npc_gear/backpack.glb",
|
||||||
"triangles": 316,
|
"triangles": 380,
|
||||||
"vertices": 360
|
"vertices": 406
|
||||||
},
|
},
|
||||||
"beanie": {
|
"beanie": {
|
||||||
"attach": "head",
|
"attach": "head",
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 35 KiB |
@@ -200,7 +200,8 @@ carry baked AO and the shared neutral material maps described above.
|
|||||||
the legs through the hem.
|
the legs through the hem.
|
||||||
- Preview colours (`PREVIEW_TINTS`) only stand in for `outfit_for()`. The GLBs
|
- Preview colours (`PREVIEW_TINTS`) only stand in for `outfit_for()`. The GLBs
|
||||||
stay neutral.
|
stay neutral.
|
||||||
- The 0.9.4 baseline found three P1 portrait issues: the tourist's photo prop
|
- The 0.9.4 baseline found three P1 portrait issues. The tourist's photo prop
|
||||||
duplicates the backpack silhouette, the face is shallow/faceted at portrait
|
now only appears in photo/film poses and the backpack straps were refit as
|
||||||
range, and the backpack straps read as rigid bars. The full ranked list and
|
flattened chest bands; the face is still shallow/faceted at portrait range.
|
||||||
accepted/deferred items are in [`npc-visual-review.md`](npc-visual-review.md).
|
The full ranked list, measurements and before/after sheet are in
|
||||||
|
[`npc-visual-review.md`](npc-visual-review.md).
|
||||||
@@ -44,6 +44,7 @@ Committed contact sheets and local Blender renders for the character, gear and p
|
|||||||
| <img src="sedan.png" width="280" alt="sedan.png"> | [`sedan.png`](sedan.png) |
|
| <img src="sedan.png" width="280" alt="sedan.png"> | [`sedan.png`](sedan.png) |
|
||||||
| <img src="sheet_npc.jpg" width="280" alt="sheet_npc.jpg"> | [`sheet_npc.jpg`](sheet_npc.jpg) |
|
| <img src="sheet_npc.jpg" width="280" alt="sheet_npc.jpg"> | [`sheet_npc.jpg`](sheet_npc.jpg) |
|
||||||
| <img src="sheet_npc_baseline.jpg" width="280" alt="sheet_npc_baseline.jpg"> | [`sheet_npc_baseline.jpg`](sheet_npc_baseline.jpg) |
|
| <img src="sheet_npc_baseline.jpg" width="280" alt="sheet_npc_baseline.jpg"> | [`sheet_npc_baseline.jpg`](sheet_npc_baseline.jpg) |
|
||||||
|
| <img src="sheet_npc_baseline_compare-p1.jpg" width="280" alt="sheet_npc_baseline_compare-p1.jpg"> | [`sheet_npc_baseline_compare-p1.jpg`](sheet_npc_baseline_compare-p1.jpg) |
|
||||||
| <img src="sheet_npc_gear.jpg" width="280" alt="sheet_npc_gear.jpg"> | [`sheet_npc_gear.jpg`](sheet_npc_gear.jpg) |
|
| <img src="sheet_npc_gear.jpg" width="280" alt="sheet_npc_gear.jpg"> | [`sheet_npc_gear.jpg`](sheet_npc_gear.jpg) |
|
||||||
| <img src="sheet_props.jpg" width="280" alt="sheet_props.jpg"> | [`sheet_props.jpg`](sheet_props.jpg) |
|
| <img src="sheet_props.jpg" width="280" alt="sheet_props.jpg"> | [`sheet_props.jpg`](sheet_props.jpg) |
|
||||||
| <img src="shoulder_bag.png" width="280" alt="shoulder_bag.png"> | [`shoulder_bag.png`](shoulder_bag.png) |
|
| <img src="shoulder_bag.png" width="280" alt="shoulder_bag.png"> | [`shoulder_bag.png`](shoulder_bag.png) |
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 298 KiB |
@@ -5,7 +5,7 @@ Implemented so far:
|
|||||||
- Godot 4.7.2 project scaffold, input map, procedural world builder (CI/local/release unified)
|
- Godot 4.7.2 project scaffold, input map, procedural world builder (CI/local/release unified)
|
||||||
- Venue-driven simulation core: `ShiftCoordinator`, `VenueScenario`, `EventScheduler`, `NPCController` + FSM, `AccessVerifier`, `EscalationManager`, `RadioSystem`, `IncidentRecorder`, `ReputationEvaluator`, `CrowdSystem`
|
- Venue-driven simulation core: `ShiftCoordinator`, `VenueScenario`, `EventScheduler`, `NPCController` + FSM, `AccessVerifier`, `EscalationManager`, `RadioSystem`, `IncidentRecorder`, `ReputationEvaluator`, `CrowdSystem`
|
||||||
- Three authored scenarios (El Capitan Entertainment Centre / Hollywood Boulevard, The Joke Shop, Late Night TV Studio) with access policies, NPC archetypes, and event pools
|
- Three authored scenarios (El Capitan Entertainment Centre / Hollywood Boulevard, The Joke Shop, Late Night TV Studio) with access policies, NPC archetypes, and event pools
|
||||||
- Headless unit tests: **261 passing** (access rules and venue-correct access loadout, escalation, radio cooldown/battery, NPC reaction determinism, door lock, flashlight perception, persistence round-trip, settings round-trip, input remaps, navmesh, step colliders, skyline alpha, scenario data, full-shift simulation, wand screening, responder dispatch, localization strings, focus cone, action wheel, session shell, PLAN verbs and incident recording, crowd presentation, emergency aftermath, body-camera timeline, NPC theater poses, audio, accessibility, authored scenario content, string formatting, `.tres` venue round-trip, surface materials and environment, focus ranking and outfit/gait helpers, generated prop models, web export preset, prop atlas material, player movement, NPC character layers/LOD/queue, input bindings)
|
- Headless unit tests: **262 passing** (access rules and venue-correct access loadout, escalation, radio cooldown/battery, NPC reaction determinism, door lock, flashlight perception, persistence round-trip, settings round-trip, input remaps, navmesh, step colliders, skyline alpha, scenario data, full-shift simulation, wand screening, responder dispatch, localization strings, focus cone, action wheel, session shell, PLAN verbs and incident recording, crowd presentation, emergency aftermath, body-camera timeline, NPC theater poses, audio, accessibility, authored scenario content, string formatting, `.tres` venue round-trip, surface materials and environment, focus ranking and outfit/gait helpers, generated prop models, web export preset, prop atlas material, player movement, NPC character layers/LOD/queue, input bindings)
|
||||||
- Simulation extras: protester archetype, CALL_SOMEONE reaction, door lock/secure mechanic (visual indicator), flashlight with night perception bonus, LATE shift phase reaching the violent-threat event
|
- Simulation extras: protester archetype, CALL_SOMEONE reaction, door lock/secure mechanic (visual indicator), flashlight with night perception bonus, LATE shift phase reaching the violent-threat event
|
||||||
- First-person prototype with interaction HUD, access inspection, wand screening, radio report form (incl. emergency hold-to-confirm), dispatched responder avatars, aim-free targeting, venue cycling between all three scenarios, color-coded escalation feedback, procedural audio cues, navigation-mesh NPC paths, JSON-backed localized strings, persisted shift history, debug overlay, and shift review
|
- First-person prototype with interaction HUD, access inspection, wand screening, radio report form (incl. emergency hold-to-confirm), dispatched responder avatars, aim-free targeting, venue cycling between all three scenarios, color-coded escalation feedback, procedural audio cues, navigation-mesh NPC paths, JSON-backed localized strings, persisted shift history, debug overlay, and shift review
|
||||||
- Control pass: focus cone on NPC heads with a billboarded blue circle in front of the face and name, action wheel (LMB/Space) with de-escalation-first slices, mouse-look freeze while a panel is open, acceleration-based walking, and named input actions (`action_menu`, `confirm`, `cancel`, `inspect`, `wand`)
|
- Control pass: focus cone on NPC heads with a billboarded blue circle in front of the face and name, action wheel (LMB/Space) with de-escalation-first slices, mouse-look freeze while a panel is open, acceleration-based walking, and named input actions (`action_menu`, `confirm`, `cancel`, `inspect`, `wand`)
|
||||||
@@ -39,6 +39,7 @@ Implemented so far:
|
|||||||
- Aim-free selection pass (v0.3.0 work, [ADR-0011](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
|
- Aim-free selection pass (v0.3.0 work, [ADR-0011](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
|
||||||
|
|
||||||
- Visual alignment: start/venue menu, pause, settings, shift review, radio, wand, inspection, timeline, HUD target chip/escalation bar, and radial menu now use or match their AI references in `assets/images/ui/`. The runtime art loader (`src/game/ui/art.gd`) falls back cleanly when a file is missing. The wand panel lists the guest's carried items, and multiline HUD/wand strings render real line breaks.
|
- Visual alignment: start/venue menu, pause, settings, shift review, radio, wand, inspection, timeline, HUD target chip/escalation bar, and radial menu now use or match their AI references in `assets/images/ui/`. The runtime art loader (`src/game/ui/art.gd`) falls back cleanly when a file is missing. The wand panel lists the guest's carried items, and multiline HUD/wand strings render real line breaks.
|
||||||
|
- NPC P1 portrait fixes (0.9.4 work): tourists now hide their photo-pose camera bag outside `photo`/`film` poses, removing the duplicate-backpack silhouette; the backpack gear was rebuilt with flattened, forward chest straps and a fresh AO map. The before/after sheet is [`blender_previews/sheet_npc_baseline_compare-p1.jpg`](blender_previews/sheet_npc_baseline_compare-p1.jpg); the face and remaining clothing/hand work stay open.
|
||||||
- NPC visual baseline (0.9.4 work): `src/test/capture_npc_baseline.gd` captures all eight library characters from front, three-quarter, profile, back, full-body and crowd views in a neutral studio (48 frames). The criteria, measurements and ranked gaps are in [`npc-visual-review.md`](npc-visual-review.md); the committed sheet is [`blender_previews/sheet_npc_baseline.jpg`](blender_previews/sheet_npc_baseline.jpg). No model changes were made in this baseline step.
|
- NPC visual baseline (0.9.4 work): `src/test/capture_npc_baseline.gd` captures all eight library characters from front, three-quarter, profile, back, full-body and crowd views in a neutral studio (48 frames). The criteria, measurements and ranked gaps are in [`npc-visual-review.md`](npc-visual-review.md); the committed sheet is [`blender_previews/sheet_npc_baseline.jpg`](blender_previews/sheet_npc_baseline.jpg). No model changes were made in this baseline step.
|
||||||
- UI-state capture pass (0.9.3 work): `src/test/capture_ui_states.gd` adds committed captures 14–21 for the start menu, pause, settings, shift review, emergency radio, action wheel, crowd panic and responder arrival. Every one of the 21 catalogue entries now has a current-build capture; `tools/check_screenshots.sh` reports 21 good / 0 bad and the automatic judge reports 21/21 match, 0 diff. Crowd panic and responder arrival are staged presentation frames, not gameplay changes.
|
- UI-state capture pass (0.9.3 work): `src/test/capture_ui_states.gd` adds committed captures 14–21 for the start menu, pause, settings, shift review, emergency radio, action wheel, crowd panic and responder arrival. Every one of the 21 catalogue entries now has a current-build capture; `tools/check_screenshots.sh` reports 21 good / 0 bad and the automatic judge reports 21/21 match, 0 diff. Crowd panic and responder arrival are staged presentation frames, not gameplay changes.
|
||||||
- Realism adoption (post-v0.6.0): v2 per-phase skylines and surface tiles with normal maps, retuned sun/exposure/fog, wet asphalt, denser foliage, a visible additive flashlight beam, and refreshed staging captures. `tools/check_screenshots.sh` is the repeatable capture/compare gate; judge is clean of `diff` verdicts.
|
- Realism adoption (post-v0.6.0): v2 per-phase skylines and surface tiles with normal maps, retuned sun/exposure/fog, wet asphalt, denser foliage, a visible additive flashlight beam, and refreshed staging captures. `tools/check_screenshots.sh` is the repeatable capture/compare gate; judge is clean of `diff` verdicts.
|
||||||
|
|||||||
+30
-23
@@ -1,8 +1,10 @@
|
|||||||
# NPC visual baseline and acceptance criteria
|
# NPC visual baseline and acceptance criteria
|
||||||
|
|
||||||
Status: baseline captured 2026-11-02. No character GLB, texture, gear or runtime
|
Status: baseline captured 2026-11-02; the first P1 fixes were applied the same
|
||||||
mesh was changed in this step. The baseline is the reference for the 0.9.4 NPC
|
day (tourist photo-prop visibility and backpack strap geometry). The face pass
|
||||||
visual optimization.
|
and the remaining body/clothing work are still open. The baseline remains the
|
||||||
|
reference for the 0.9.4 NPC visual optimization; the post-fix comparison is
|
||||||
|
[`blender_previews/sheet_npc_baseline_compare-p1.jpg`](blender_previews/sheet_npc_baseline_compare-p1.jpg).
|
||||||
|
|
||||||
## What was captured
|
## What was captured
|
||||||
|
|
||||||
@@ -99,27 +101,32 @@ From `python3 tools/blender_assets.py fit` and the manifest:
|
|||||||
|
|
||||||
The widest character is `npc_coat` at 5,026 triangles and 4,807 vertices, so
|
The widest character is `npc_coat` at 5,026 triangles and 4,807 vertices, so
|
||||||
the current headroom is 974 triangles and 193 vertices per character. Any face
|
the current headroom is 974 triangles and 193 vertices per character. Any face
|
||||||
or hand refinement must come out of that budget or replace existing detail.
|
or hand refinement must come out of that budget or replace existing detail. The
|
||||||
|
refit backpack measures 380 triangles / 406 vertices; the character and gear
|
||||||
|
table above remains valid because the character meshes did not change.
|
||||||
|
|
||||||
## Baseline findings, ranked
|
## Baseline findings, ranked
|
||||||
|
|
||||||
### P1 — fix in the first model pass
|
### P1 — fix in the first model pass
|
||||||
|
|
||||||
1. **The tourist carries two bag silhouettes.** `_add_props()` creates a
|
1. **The tourist carried two bag silhouettes. Fixed 2026-11-02.** `_add_props()`
|
||||||
0.30 × 0.36 × 0.14 box at `(-0.34, 1.15, -0.18)` for the tourist photo pose
|
created a 0.30 × 0.36 × 0.14 box at `(-0.34, 1.15, -0.18)` for the tourist
|
||||||
while `_add_accessory("backpack")` also attaches the library backpack. From
|
photo pose while `_add_accessory("backpack")` also attached the library
|
||||||
the three-quarter/back view the two read as separate bags. The photo prop
|
backpack. From the three-quarter/back view the two read as separate bags. The
|
||||||
should be hidden outside the photo pose, merged into the backpack, or moved to
|
photo prop is now hidden outside `photo`/`film` poses
|
||||||
a clearly held camera.
|
(`photo_prop_visible_for_pose()`), so it only appears when the character is
|
||||||
|
actually taking a picture; the after frame confirms one bag at rest.
|
||||||
2. **The face is the largest portrait gap.** The eyes, nose and lips are
|
2. **The face is the largest portrait gap.** The eyes, nose and lips are
|
||||||
readable but sit on a shallow, faceted head: the eye whites protrude, the
|
readable but sit on a shallow, faceted head: the eye whites protrude, the
|
||||||
eyelid/cheek transition is missing, and the mouth is a dark slot. This is
|
eyelid/cheek transition is missing, and the mouth is a dark slot. This is
|
||||||
intentionally stylized, but the head volume and eye socket are the first
|
intentionally stylized, but the head volume and eye socket are the first
|
||||||
things a player sees at conversation range.
|
things a player sees at conversation range.
|
||||||
3. **Backpack straps read as rigid black bars.** The back gear's straps cross
|
3. **Backpack straps read as rigid black bars. Refit 2026-11-02.** The straps
|
||||||
the chest as thin, straight geometry with visible gaps from the shoulders,
|
were thin round tubes whose chest/waist points sat inside the torso, so only
|
||||||
not as fabric sitting on the torso. A player sees this on the most common
|
fragments showed from the front. They are now flattened bands whose front path
|
||||||
tourist silhouette in every crowd.
|
sits outside the chest; the after sheet shows a continuous strap instead of
|
||||||
|
two detached rods. The backpack asset is now 380 triangles / 406 vertices
|
||||||
|
(still far below the 1,500-triangle gear budget).
|
||||||
|
|
||||||
### P2 — fix after the first P1 pass
|
### P2 — fix after the first P1 pass
|
||||||
|
|
||||||
@@ -167,17 +174,17 @@ python3 tools/npc_baseline.py compare \
|
|||||||
```
|
```
|
||||||
|
|
||||||
The comparison sheet places the before/after pair for each view side by side.
|
The comparison sheet places the before/after pair for each view side by side.
|
||||||
If the change is accepted, copy the after sheet to a new committed name
|
The first run is committed as
|
||||||
(`sheet_npc_baseline_after.jpg`) and link both from the preview index. The
|
[`sheet_npc_baseline_compare-p1.jpg`](blender_previews/sheet_npc_baseline_compare-p1.jpg);
|
||||||
baseline is not replaced.
|
keep the baseline sheet as the before artifact and add a new numbered/after
|
||||||
|
sheet rather than replacing it.
|
||||||
|
|
||||||
## Next implementation order
|
## Next implementation order
|
||||||
|
|
||||||
1. Remove the duplicate tourist bag silhouette and refit the backpack straps.
|
1. Refine the head/eye socket/jaw volume without changing the face direction or
|
||||||
2. Refine the head/eye socket/jaw volume without changing the face direction or
|
|
||||||
the five tint slots.
|
the five tint slots.
|
||||||
3. Add a conversation-distance hand/grip capture and fix hand geometry before
|
2. Add a conversation-distance hand/grip capture and fix hand geometry before
|
||||||
judging hand items.
|
judging hand items.
|
||||||
4. Audit hems and footwear in walk/sit/block poses.
|
3. Audit hems and footwear in walk/sit/block poses.
|
||||||
5. Re-run the 48-frame baseline, the 96-frame final-context review, the 24-NPC
|
4. Re-run the 48-frame baseline, the 96-frame final-context review, the 24-NPC
|
||||||
budget and `blender_assets.py audit/fit`; publish the before/after sheet.
|
budget and `blender_assets.py audit/fit`; publish the next comparison sheet.
|
||||||
+17
-1
@@ -114,6 +114,7 @@ var _focus_circle: MeshInstance3D = null
|
|||||||
var _name_tag: Label3D = null
|
var _name_tag: Label3D = null
|
||||||
## Theater props and limbs for the idle poses (ADR-0010 rig).
|
## Theater props and limbs for the idle poses (ADR-0010 rig).
|
||||||
var _photo_prop: MeshInstance3D = null
|
var _photo_prop: MeshInstance3D = null
|
||||||
|
var _photo_flash: MeshInstance3D = null
|
||||||
var _sign_prop: MeshInstance3D = null
|
var _sign_prop: MeshInstance3D = null
|
||||||
var _legs: Array[Node3D] = []
|
var _legs: Array[Node3D] = []
|
||||||
var _arms: Array[Node3D] = []
|
var _arms: Array[Node3D] = []
|
||||||
@@ -891,8 +892,11 @@ func _add_props(npc_type: int) -> void:
|
|||||||
match npc_type:
|
match npc_type:
|
||||||
Rules.NpcType.INFLUENCER:
|
Rules.NpcType.INFLUENCER:
|
||||||
_photo_prop = _add_box_prop(_rig, Vector3(0.16, 0.1, 0.09), Vector3(0.3, chest_y, 0.22), Color(0.1, 0.1, 0.1))
|
_photo_prop = _add_box_prop(_rig, Vector3(0.16, 0.1, 0.09), Vector3(0.3, chest_y, 0.22), Color(0.1, 0.1, 0.1))
|
||||||
|
_photo_prop.name = "PhotoProp"
|
||||||
|
_photo_prop.visible = false
|
||||||
_photo_prop_base_y = _photo_prop.position.y
|
_photo_prop_base_y = _photo_prop.position.y
|
||||||
_add_box_prop(_rig, Vector3(0.05, 0.05, 0.06), Vector3(0.34, chest_y + 0.06, 0.30), Color(0.4, 0.7, 1.0))
|
_photo_flash = _add_box_prop(_rig, Vector3(0.05, 0.05, 0.06), Vector3(0.34, chest_y + 0.06, 0.30), Color(0.4, 0.7, 1.0))
|
||||||
|
_photo_flash.visible = false
|
||||||
Rules.NpcType.DELIVERY:
|
Rules.NpcType.DELIVERY:
|
||||||
_add_box_prop(_rig, Vector3(0.34, 0.26, 0.28), Vector3(0.0, 1.02, 0.3), Color(0.55, 0.4, 0.25))
|
_add_box_prop(_rig, Vector3(0.34, 0.26, 0.28), Vector3(0.0, 1.02, 0.3), Color(0.55, 0.4, 0.25))
|
||||||
Rules.NpcType.PERFORMER:
|
Rules.NpcType.PERFORMER:
|
||||||
@@ -917,6 +921,8 @@ func _add_props(npc_type: int) -> void:
|
|||||||
_sign_prop_base_y = _sign_prop.position.y
|
_sign_prop_base_y = _sign_prop.position.y
|
||||||
Rules.NpcType.TOURIST:
|
Rules.NpcType.TOURIST:
|
||||||
_photo_prop = _add_box_prop(_rig, Vector3(0.3, 0.36, 0.14), Vector3(-0.34, 1.15, -0.18), Color(0.25, 0.3, 0.45))
|
_photo_prop = _add_box_prop(_rig, Vector3(0.3, 0.36, 0.14), Vector3(-0.34, 1.15, -0.18), Color(0.25, 0.3, 0.45))
|
||||||
|
_photo_prop.name = "PhotoProp"
|
||||||
|
_photo_prop.visible = false
|
||||||
_photo_prop_base_y = _photo_prop.position.y
|
_photo_prop_base_y = _photo_prop.position.y
|
||||||
Rules.NpcType.CONFUSED_VISITOR:
|
Rules.NpcType.CONFUSED_VISITOR:
|
||||||
_add_box_prop(_rig, Vector3(0.3, 0.05, 0.34), Vector3(0, 0.92, 0.06), Color(0.9, 0.88, 0.8))
|
_add_box_prop(_rig, Vector3(0.3, 0.05, 0.34), Vector3(0, 0.92, 0.06), Color(0.9, 0.88, 0.8))
|
||||||
@@ -1042,6 +1048,12 @@ static func prop_lift_for_pose(pose: String, phase: float) -> float:
|
|||||||
return 0.0
|
return 0.0
|
||||||
return maxf(0.0, sin(phase * 1.7)) * 0.16
|
return maxf(0.0, sin(phase * 1.7)) * 0.16
|
||||||
|
|
||||||
|
## The tourist's camera bag and the influencer's chest camera are only part of
|
||||||
|
## the silhouette while the character is actually taking photos. Keeping them
|
||||||
|
## hidden at other times prevents the tourist from reading as two backpacks.
|
||||||
|
static func photo_prop_visible_for_pose(pose: String) -> bool:
|
||||||
|
return pose == "photo" or pose == "film"
|
||||||
|
|
||||||
## Opposite arm/leg swing for the walk cycle. `side` is 0 or 1; pure so the gait
|
## Opposite arm/leg swing for the walk cycle. `side` is 0 or 1; pure so the gait
|
||||||
## can be checked without a scene tree.
|
## can be checked without a scene tree.
|
||||||
static func limb_swing(phase: float, side: int, amplitude: float) -> float:
|
static func limb_swing(phase: float, side: int, amplitude: float) -> float:
|
||||||
@@ -1060,6 +1072,10 @@ func _animate(delta: float) -> void:
|
|||||||
var pose := theater_pose()
|
var pose := theater_pose()
|
||||||
var escalation: int = npc.highest_escalation_received if npc != null else 0
|
var escalation: int = npc.highest_escalation_received if npc != null else 0
|
||||||
var crouch := crouch_for_pose(pose)
|
var crouch := crouch_for_pose(pose)
|
||||||
|
if _photo_prop:
|
||||||
|
_photo_prop.visible = photo_prop_visible_for_pose(pose)
|
||||||
|
if _photo_flash:
|
||||||
|
_photo_flash.visible = photo_prop_visible_for_pose(pose)
|
||||||
if speed > 0.3:
|
if speed > 0.3:
|
||||||
_idle_t = 0.0
|
_idle_t = 0.0
|
||||||
_bob_phase += delta * (6.0 + speed * 1.7)
|
_bob_phase += delta * (6.0 + speed * 1.7)
|
||||||
|
|||||||
@@ -47,6 +47,14 @@ func test_hand_items_are_known_and_deterministic() -> bool:
|
|||||||
Rules.NpcType.TOURIST, seed_value * 31)
|
Rules.NpcType.TOURIST, seed_value * 31)
|
||||||
return TestUtils.check(ok, "hand items stay in the catalogue and are deterministic")
|
return TestUtils.check(ok, "hand items stay in the catalogue and are deterministic")
|
||||||
|
|
||||||
|
func test_photo_prop_only_shows_for_camera_poses() -> bool:
|
||||||
|
return TestUtils.check(
|
||||||
|
not NpcAvatar.photo_prop_visible_for_pose("idle")
|
||||||
|
and not NpcAvatar.photo_prop_visible_for_pose("perform")
|
||||||
|
and NpcAvatar.photo_prop_visible_for_pose("photo")
|
||||||
|
and NpcAvatar.photo_prop_visible_for_pose("film"),
|
||||||
|
"camera bag prop is hidden outside photo/film poses")
|
||||||
|
|
||||||
func test_idle_styles_are_known() -> bool:
|
func test_idle_styles_are_known() -> bool:
|
||||||
var allowed := ["phone", "crossed", "look", "clipboard", "breathe"]
|
var allowed := ["phone", "crossed", "look", "clipboard", "breathe"]
|
||||||
var ok := true
|
var ok := true
|
||||||
|
|||||||
+18
-5
@@ -412,8 +412,13 @@ def lathe(name, profile, mat, seg=16, loc=(0, 0, 0), smooth=True, sharp=30.0,
|
|||||||
return _finish(_link(name, bm), mat, smooth=smooth, sharp_angle=sharp)
|
return _finish(_link(name, bm), mat, smooth=smooth, sharp_angle=sharp)
|
||||||
|
|
||||||
|
|
||||||
def tube(name, points, radius, mat, seg=8, caps=True):
|
def tube(name, points, radius, mat, seg=8, caps=True, width_scale=1.0,
|
||||||
"""Bent tube through 3D points (handrails, umbrella handles, frames)."""
|
thickness_scale=1.0):
|
||||||
|
"""Bent tube through 3D points (handrails, umbrella handles, frames).
|
||||||
|
|
||||||
|
`width_scale`/`thickness_scale` turn the round cross-section into a flat
|
||||||
|
strap for shoulder harnesses; the default keeps the old round tube.
|
||||||
|
"""
|
||||||
bm = bmesh.new()
|
bm = bmesh.new()
|
||||||
rings = []
|
rings = []
|
||||||
pts = [Vector(p) for p in points]
|
pts = [Vector(p) for p in points]
|
||||||
@@ -430,7 +435,9 @@ def tube(name, points, radius, mat, seg=8, caps=True):
|
|||||||
ring = []
|
ring = []
|
||||||
for k in range(seg):
|
for k in range(seg):
|
||||||
a = math.tau * k / seg
|
a = math.tau * k / seg
|
||||||
ring.append(bm.verts.new(p + radius * (math.cos(a) * side + math.sin(a) * up2)))
|
ring.append(bm.verts.new(p + radius * (
|
||||||
|
math.cos(a) * side * width_scale
|
||||||
|
+ math.sin(a) * up2 * thickness_scale)))
|
||||||
rings.append(ring)
|
rings.append(ring)
|
||||||
for a, b in zip(rings, rings[1:]):
|
for a, b in zip(rings, rings[1:]):
|
||||||
for k in range(seg):
|
for k in range(seg):
|
||||||
@@ -1008,9 +1015,15 @@ def build_gear(name):
|
|||||||
bevel=0.02, segs=2))
|
bevel=0.02, segs=2))
|
||||||
p.append(box("Zip", (0.24, 0.005, 0.01), (0, 0.317, 0.05), M("steel")))
|
p.append(box("Zip", (0.24, 0.005, 0.01), (0, 0.317, 0.05), M("steel")))
|
||||||
for s in (-1.0, 1.0):
|
for s in (-1.0, 1.0):
|
||||||
|
# Strap path: over the shoulder, then down the chest outside the
|
||||||
|
# torso front (Godot z≈0.21). The old chest/waist points sat at
|
||||||
|
# z≈0.13 inside the body, so only thin tube fragments read as two
|
||||||
|
# rigid black bars from the front.
|
||||||
p.append(tube("Strap", [(0.09 * s, 0.13, 0.27), (0.10 * s, -0.02, 0.29),
|
p.append(tube("Strap", [(0.09 * s, 0.13, 0.27), (0.10 * s, -0.02, 0.29),
|
||||||
(0.11 * s, -0.13, 0.18), (0.12 * s, -0.12, -0.08)],
|
(0.105 * s, -0.06, 0.25),
|
||||||
0.012, M("plastic_black"), seg=6))
|
(0.11 * s, -0.19, 0.18), (0.12 * s, -0.19, -0.08)],
|
||||||
|
0.018, M("plastic_black"), seg=8, width_scale=1.35,
|
||||||
|
thickness_scale=0.42))
|
||||||
elif name == "shoulder_bag":
|
elif name == "shoulder_bag":
|
||||||
# Keep the bag's inner face outside the widest skirt flare (dress = 0.26):
|
# Keep the bag's inner face outside the widest skirt flare (dress = 0.26):
|
||||||
# 0.292 - 0.06/2 = 0.262. The strap starts/ends on that same outboard line.
|
# 0.292 - 0.06/2 = 0.262. The strap starts/ends on that same outboard line.
|
||||||
|
|||||||
Reference in new issue
Block a user