Tune the flashlight against measured luminance and refresh the venue gallery
No files matched your search
@@ -6,6 +6,12 @@ Semantic Versioning.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- The flashlight actually lights what it points at. It was tuned to a beam so weak it changed the
|
||||||
|
frame by less than one 255th of a level; it now throws a visible pool on the door and the pavement
|
||||||
|
ahead, in both venues.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Street greenery now reads as leaves instead of green rocks: the planter shrubs and tree crowns
|
- Street greenery now reads as leaves instead of green rocks: the planter shrubs and tree crowns
|
||||||
|
|||||||
@@ -130,6 +130,16 @@ canonical implemented-features list in `docs/project/development-status.md`.
|
|||||||
gear library. The procedural library + MakeHuman head stay in the tree as the tested fallback
|
gear library. The procedural library + MakeHuman head stay in the tree as the tested fallback
|
||||||
(`build --source procedural`), not in the shipped roster. Silhouette variety is the known
|
(`build --source procedural`), not in the shipped roster. Silhouette variety is the known
|
||||||
regression — three meshes for twelve roles — and the modular part library is the `0.9.10` fix.
|
regression — three meshes for twelve roles — and the modular part library is the `0.9.10` fix.
|
||||||
|
- **Tune lights against a measurement, not a look (2026-11-06):** the shipped flashlight changed the
|
||||||
|
late-night frame by 0.1/255 and nobody noticed. `capture_realism.gd --stages=late --flashlight`
|
||||||
|
renders the same frame with the lamp on and off; mean luminance of the aimed region and the near
|
||||||
|
pavement is now the acceptance number. Current: energy 10, range 45 m, cone 24°, tilted 5° down →
|
||||||
|
13.5 → 29.7 aimed, 3.7 → 11.7 near ground.
|
||||||
|
- **Docs live in category folders (2026-11-06):** `docs/project/` (architecture, development status),
|
||||||
|
`docs/design/` (gameplay, venues, ui-copy, accessibility, known-limitations), `docs/assets/`
|
||||||
|
(asset tooling, blender assets, npc sourcing, 3d-model-review, makehuman head), `docs/process/`
|
||||||
|
(getting started, release QA, releasing), plus `docs/adr/`, `docs/screens/`, `docs/screenshots/`.
|
||||||
|
Write to the new paths; `docs/*.md` at the root no longer exists apart from `README.md`.
|
||||||
- **Foliage is leaf cards, not sphere stacks (2026-11-06):** `_shrub()` is one low-poly inner mass
|
- **Foliage is leaf cards, not sphere stacks (2026-11-06):** `_shrub()` is one low-poly inner mass
|
||||||
plus 18–53 two-triangle cards scattered on the canopy shell by a jittered Fibonacci sequence, each
|
plus 18–53 two-triangle cards scattered on the canopy shell by a jittered Fibonacci sequence, each
|
||||||
rotated onto the canopy normal and tilted off it. No alpha texture, so the flat-colour library and
|
rotated onto the canopy normal and tilted off it. No alpha texture, so the flat-colour library and
|
||||||
|
|||||||
@@ -957,8 +957,17 @@ scorecard source, this milestone is the actionable list.
|
|||||||
|
|
||||||
- [ ] Joke Shop / Hollywood shape language: fix the ranked remaining items (west rounded corner,
|
- [ ] Joke Shop / Hollywood shape language: fix the ranked remaining items (west rounded corner,
|
||||||
canopy pitch, facade reveal depth) only where a capture shows a readable gain.
|
canopy pitch, facade reveal depth) only where a capture shows a readable gain.
|
||||||
- [ ] `lighting`/`lookdev` pass: flashlight cone/energy for the `06-night-flashlight` stage,
|
- [x] **Flashlight tuned by measurement.** The late-night capture proved it did nothing: energy 4.2,
|
||||||
marquee/event-banner emissive read under the Compatibility renderer, street-lamp pools.
|
20 m range and a 14° cone changed the frame by **0.1/255**. Now 10 energy, 45 m, 24°, tilted 5°
|
||||||
|
down — the aimed region goes 13.5 → 29.7 and the near pavement 3.7 → 11.7 (0–255), and the
|
||||||
|
committed `06-night-flashlight` shot shows the beam on the steps and the NPC in front.
|
||||||
|
- [ ] Marquee/event-banner emissive read under the Compatibility renderer: measure the banner
|
||||||
|
luminance against the reference night board and adjust only if it still clips.
|
||||||
|
- [ ] Street-lamp pools: the night ground sits at 3.8/255 away from the sconces. Raising the lamp
|
||||||
|
energy was rejected once for washing the sidewalk grey, so this needs a capture-driven
|
||||||
|
range/attenuation change rather than more energy.
|
||||||
|
- [x] Refresh `docs/screenshots/` for the whole roster and the new flashlight; `check_screenshots.sh`
|
||||||
|
reports 21 good / 0 bad.
|
||||||
- [ ] Set dressing: fix the Joke Shop queue story (podium/stanchion line at the door), rope
|
- [ ] Set dressing: fix the Joke Shop queue story (podium/stanchion line at the door), rope
|
||||||
barrier thickness, the west palm overlap and the empty sidewalk light pool; apply the same
|
barrier thickness, the west palm overlap and the empty sidewalk light pool; apply the same
|
||||||
review to Hollywood and the TV Studio.
|
review to Hollywood and the TV Studio.
|
||||||
@@ -966,8 +975,7 @@ scorecard source, this milestone is the actionable list.
|
|||||||
|
|
||||||
### Evidence and release
|
### Evidence and release
|
||||||
|
|
||||||
- [ ] Refresh `sheet_props.jpg`, `sheet_npc_gear.jpg`, the three-up boards, the affected venue
|
- [x] `sheet_props.jpg` and `sheet_npc_gear.jpg` rebuilt; the 21-image venue gallery regenerated.
|
||||||
captures and the README asset rows.
|
|
||||||
- [ ] Re-run `tools/check_screenshots.sh` and `python3 tools/check_docs.py`; record both results.
|
- [ ] Re-run `tools/check_screenshots.sh` and `python3 tools/check_docs.py`; record both results.
|
||||||
- [ ] Release `0.9.11` with the full procedure.
|
- [ ] Release `0.9.11` with the full procedure.
|
||||||
|
|
||||||
|
|||||||
@@ -302,6 +302,25 @@ renderer are untouched.
|
|||||||
| `street_tree` | 2,592 tris | 1,506 |
|
| `street_tree` | 2,592 tris | 1,506 |
|
||||||
| Props total (20) | 13,808 | 11,784 |
|
| Props total (20) | 13,808 | 11,784 |
|
||||||
|
|
||||||
|
## Lighting pass — flashlight measured 2026-11-06
|
||||||
|
|
||||||
|
`capture_realism.gd --stages=late --flashlight` renders the same frame with the lamp off and on, so
|
||||||
|
the beam's contribution is a number instead of an impression. Mean luminance, 0–255:
|
||||||
|
|
||||||
|
| Region | Lamp off | Old settings | Shipped now |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Aimed centre (facade) | 13.5 | 13.6 | **29.7** |
|
||||||
|
| Near pavement | 3.7 | 3.7 | **11.7** |
|
||||||
|
|
||||||
|
The old flashlight (energy 4.2, 20 m range, 14° cone, aimed dead ahead) moved the frame by 0.1/255 —
|
||||||
|
it was effectively off. The shipped values are 10 energy, 45 m, 24°, tilted 5° down: the beam lands
|
||||||
|
on the door or person being checked *and* puts a pool on the pavement in front, without flooding the
|
||||||
|
facade. Verified in both venues (Hollywood facade and the Joke Shop door).
|
||||||
|
|
||||||
|
Still open from the milestone: marquee emissive under Compatibility, and the street-lamp pools — the
|
||||||
|
night ground sits at 3.8/255 away from the sconces, and raising lamp energy was already rejected once
|
||||||
|
for washing the sidewalk grey, so those need range/attenuation work rather than more brightness.
|
||||||
|
|
||||||
## Scorecard skeleton
|
## Scorecard skeleton
|
||||||
|
|
||||||
Verdicts use `qa-review` semantics: **P0** (blocker: wrong scale, broken normals, budget fail),
|
Verdicts use `qa-review` semantics: **P0** (blocker: wrong scale, broken normals, budget fail),
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 523 KiB After Width: | Height: | Size: 522 KiB |
|
Before Width: | Height: | Size: 612 KiB After Width: | Height: | Size: 682 KiB |
|
Before Width: | Height: | Size: 426 KiB After Width: | Height: | Size: 433 KiB |
|
Before Width: | Height: | Size: 757 KiB After Width: | Height: | Size: 757 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 377 KiB After Width: | Height: | Size: 377 KiB |
|
Before Width: | Height: | Size: 607 KiB After Width: | Height: | Size: 607 KiB |
|
Before Width: | Height: | Size: 903 KiB After Width: | Height: | Size: 916 KiB |
|
Before Width: | Height: | Size: 529 KiB After Width: | Height: | Size: 529 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
@@ -224,12 +224,17 @@ func _build_guard() -> GuardController:
|
|||||||
flashlight.name = "Flashlight"
|
flashlight.name = "Flashlight"
|
||||||
flashlight.visible = false
|
flashlight.visible = false
|
||||||
flashlight.position = Vector3(0, -0.15, -0.4)
|
flashlight.position = Vector3(0, -0.15, -0.4)
|
||||||
# Aim straight ahead: tilting it down made the cone hit the ground right in
|
# Aimed a few degrees down: straight ahead left the pavement in front of the guard
|
||||||
# front of the guard instead of the person or door being checked.
|
# black (night ground measured 3.7/255 with the lamp on), and tilting further hid
|
||||||
flashlight.rotation_degrees = Vector3.ZERO
|
# the door or person being checked.
|
||||||
flashlight.spot_range = 30.0 if Materials.modern_light_falloff() else 20.0
|
flashlight.rotation_degrees = Vector3(-5.0, 0.0, 0.0)
|
||||||
flashlight.spot_angle = 14.0
|
# Tuned against the late-night capture: the old 4.2 energy / 20 m range / 14 deg
|
||||||
flashlight.light_energy = 4.2
|
# cone changed the frame by 0.1/255 — effectively an invisible flashlight. These
|
||||||
|
# values lift the aimed region from 13.5 to 29.7 and the near pavement from 3.7 to
|
||||||
|
# 11.7 without blowing the facade out.
|
||||||
|
flashlight.spot_range = 45.0
|
||||||
|
flashlight.spot_angle = 24.0
|
||||||
|
flashlight.light_energy = 10.0
|
||||||
flashlight.spot_attenuation = 1.0
|
flashlight.spot_attenuation = 1.0
|
||||||
flashlight.spot_angle_attenuation = 0.9
|
flashlight.spot_angle_attenuation = 0.9
|
||||||
# The volumetric shaft is a Forward+ (desktop HQ) effect. Compatibility and
|
# The volumetric shaft is a Forward+ (desktop HQ) effect. Compatibility and
|
||||||
|
|||||||
@@ -38,16 +38,30 @@ func test_flashlight_aims_a_tight_forward_spot() -> bool:
|
|||||||
var guard: Node3D = gm.call("_build_guard")
|
var guard: Node3D = gm.call("_build_guard")
|
||||||
var lamp: SpotLight3D = guard.get_node_or_null("Head/Flashlight") as SpotLight3D
|
var lamp: SpotLight3D = guard.get_node_or_null("Head/Flashlight") as SpotLight3D
|
||||||
var a := TestUtils.check(lamp != null, "flashlight lives on the guard's head")
|
var a := TestUtils.check(lamp != null, "flashlight lives on the guard's head")
|
||||||
var b := TestUtils.check(lamp != null and lamp.rotation_degrees.is_equal_approx(Vector3.ZERO),
|
# The lamp is tilted a few degrees down so the beam also puts a pool on the
|
||||||
"flashlight aims straight ahead")
|
# pavement in front; straight ahead left that ground black. Anything past ~8 deg
|
||||||
var c := TestUtils.check(lamp != null and lamp.spot_angle <= 20.0,
|
# drops the door being checked out of the cone.
|
||||||
"flashlight cone is narrow enough to read as a torch")
|
var b := TestUtils.check(lamp != null
|
||||||
|
and lamp.rotation_degrees.x <= 0.0 and lamp.rotation_degrees.x >= -8.0
|
||||||
|
and is_zero_approx(lamp.rotation_degrees.y) and is_zero_approx(lamp.rotation_degrees.z),
|
||||||
|
"flashlight aims forward with a slight downward tilt (x %.1f)" % (
|
||||||
|
lamp.rotation_degrees.x if lamp else 0.0))
|
||||||
|
# Measured: the old 4.2 energy / 20 m / 14 deg beam changed the late-night frame by
|
||||||
|
# 0.1/255 and read as off. These bounds keep it a torch (not a floodlight) while
|
||||||
|
# still lighting what the guard looks at.
|
||||||
|
var c := TestUtils.check(lamp != null and lamp.spot_angle >= 20.0
|
||||||
|
and lamp.spot_angle <= 30.0,
|
||||||
|
"flashlight cone reads as a torch (%.0f deg)" % (lamp.spot_angle if lamp else 0.0))
|
||||||
|
var c2 := TestUtils.check(lamp != null and lamp.light_energy >= 8.0
|
||||||
|
and lamp.spot_range >= 30.0,
|
||||||
|
"flashlight is bright enough to see (energy %.1f, range %.0f m)" % [
|
||||||
|
lamp.light_energy if lamp else 0.0, lamp.spot_range if lamp else 0.0])
|
||||||
var d := TestUtils.check(lamp != null and not lamp.visible, "flashlight starts off")
|
var d := TestUtils.check(lamp != null and not lamp.visible, "flashlight starts off")
|
||||||
var e := TestUtils.check(lamp != null
|
var e := TestUtils.check(lamp != null
|
||||||
and is_zero_approx(lamp.light_volumetric_fog_energy),
|
and is_zero_approx(lamp.light_volumetric_fog_energy),
|
||||||
"headless/compatibility builds leave the flashlight non-volumetric")
|
"headless/compatibility builds leave the flashlight non-volumetric")
|
||||||
root.free()
|
root.free()
|
||||||
return a and b and c and d and e
|
return a and b and c and c2 and d and e
|
||||||
|
|
||||||
func test_flashlight_widens_perception() -> bool:
|
func test_flashlight_widens_perception() -> bool:
|
||||||
## Without the flashlight, an NPC at AWARE_RADIUS + 3 m is unaware.
|
## Without the flashlight, an NPC at AWARE_RADIUS + 3 m is unaware.
|
||||||
|
|||||||