docs(lighting): indoor lighting regime handoff — file #142 (windowed-interior regime) + #143 (portal dynamic light)
Clean handoff for the next M1.5 "indoor world feels right" session, picking up the two indoor-lighting gaps the user spotted at the #140 visual gate. #142 (PRIMARY): windowed-building interiors + look-ins read "like outdoors". Root cause grounded: retail's lighting regime is per-DRAW-STAGE (PView::DrawCells draws ALL EnvCells in the useSunlightSet(0) interior stage — torch-lit, no sun, regardless of SeenOutside), while acdream's is a per-FRAME global keyed on the player's cell (playerInsideCell). So acdream's windowed interiors (SeenOutside) + look-ins stay in the outdoor regime. This is the AP-43 residual surfaced. Fix direction: make sun+ambient per-draw like AP-43's torches (design fork laid out for a brainstorm). Resolves AP-43. #143 (SECONDARY): portal swirl casts no light. acdream registers only static Setup.Lights; the portal is a retail DYNAMIC light (add_dynamic_light -> minimize_envcell_lighting). Fix: register a dynamic LightSource for portals. Handoff doc carries the verified retail decomp (useSunlightSet/PView::DrawCells stages), current acdream line refs, the three gaps, the fix fork, validation plan, and DO-NOT-RETRY. Neither issue is a regression from #140. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
31d7ffd253
commit
f7f3e0887b
2 changed files with 226 additions and 0 deletions
|
|
@ -46,6 +46,44 @@ Copy this block when adding a new issue:
|
|||
|
||||
---
|
||||
|
||||
## #142 — Windowed-building interiors read "like outdoors" (indoor lighting regime is per-frame, not per-stage)
|
||||
|
||||
**Status:** OPEN
|
||||
**Severity:** MEDIUM (visible — windowed town buildings + look-ins are sun-lit/flat instead of torch-lit warm vs retail)
|
||||
**Filed:** 2026-06-20
|
||||
**Component:** render — indoor lighting regime (sun + ambient)
|
||||
|
||||
**Description (user, at the #140 gate):** The Agent of Arcanum house is much brighter/lit indoors in retail (both looking in from outside AND when inside); in acdream it is "not lit" — looking in and inside both "feel like outdoors." The meeting hall (a sealed interior) looked OK, so it's specifically WINDOWED buildings + look-ins.
|
||||
|
||||
**Root cause / status:** acdream's lighting REGIME (sun on/off + which ambient) is a per-FRAME global keyed on the PLAYER's cell (`GameWindow.cs:8107` `playerInsideCell`, from `:8061` `playerSeenOutside`, into `UpdateSunFromSky` `:8122`/`:10786`). Retail's is per-DRAW-STAGE: `PView::DrawCells` (0x005a4840) draws ALL EnvCells in the `useSunlightSet(0)` interior stage (0x005a49f3) — torch-lit, no sun — regardless of `SeenOutside`. So acdream's windowed interiors (`SeenOutside=true`) + look-ins stay in the outdoor regime (sun + outdoor ambient) where retail uses the indoor regime. This is the **AP-43 residual** made visible. Torches are already per-cell (AP-43); the SUN + AMBIENT are the remaining per-frame-global parts. **Fix direction:** make sun+ambient per-draw (per-object/cell) like AP-43's torches — needs a brainstorm (UBO second-ambient + per-instance indoor selector vs a third `uLightingMode`). Resolves AP-43.
|
||||
|
||||
**Files:** `GameWindow.cs:8061/8107/8122/10786` (regime), `mesh_modern.vert accumulateLights` (~:188/:193), `WbDrawDispatcher.IndoorObjectReceivesTorches` (:2076), `EnvCellRenderer` (mode-1).
|
||||
|
||||
**Research:** `docs/research/2026-06-20-indoor-lighting-regime-handoff.md` (full handoff — retail decomp + acdream refs + fix fork + validation plan). Register AP-43.
|
||||
|
||||
**Acceptance:** Agent of Arcanum interior torch-lit/warm both looking-in and inside (user side-by-side vs retail); sealed interiors + dungeons unchanged.
|
||||
|
||||
---
|
||||
|
||||
## #143 — Portal swirl doesn't light the room (no dynamic-light registration)
|
||||
|
||||
**Status:** OPEN
|
||||
**Severity:** LOW-MEDIUM (visible — retail's portal swirl tints the room; acdream's casts no light)
|
||||
**Filed:** 2026-06-20
|
||||
**Component:** render — dynamic point lights
|
||||
|
||||
**Description (user, at the #140 gate):** Inside the meeting hall, retail's portal swirl lights up the room; in acdream it does not.
|
||||
|
||||
**Root cause / status:** The portal swirl is a DYNAMIC light in retail (`add_dynamic_light` 0x0054d420 → `minimize_envcell_lighting` 0x0054c170 enables the cell's dynamic subset). acdream registers ONLY static `Setup.Lights` (`GameWindow.cs` ~:6404) — no dynamic lights, so the portal casts nothing. Captured retail params (predecessor cdb): `intensity=100, falloff=6, color=(0.784,0,0.784)` magenta. **Fix:** register a dynamic `LightSource` for portal-swirl entities (or read the portal model's own dat lights); it then flows through the existing point-light path and the EnvCell bake. Keep it indoor (out of the AP-43 outdoor gate).
|
||||
|
||||
**Files:** portal/particle spawn path (TBD); `GameWindow.cs` `RegisterOwnedLight` (~:6404); `LightManager` (PointSnapshot / UnregisterByOwner).
|
||||
|
||||
**Research:** `docs/research/2026-06-20-indoor-lighting-regime-handoff.md` (§#143).
|
||||
|
||||
**Acceptance:** portal swirl visibly tints the meeting-hall room vs retail.
|
||||
|
||||
---
|
||||
|
||||
## #141 — Toolbar interactivity — selected-object display
|
||||
|
||||
**Status:** IN PROGRESS (D.5.3a health + name + flash — DONE & visually confirmed 2026-06-20; mana + stack slider still deferred). Renumbered from #140 on the 2026-06-20 main merge — A7 Fix D held #140 on main; this branch's commits/spec still reference #140.
|
||||
|
|
|
|||
188
docs/research/2026-06-20-indoor-lighting-regime-handoff.md
Normal file
188
docs/research/2026-06-20-indoor-lighting-regime-handoff.md
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
# Indoor lighting regime — HANDOFF (#142 windowed-interior regime, #143 portal dynamic light)
|
||||
|
||||
**Date:** 2026-06-20 **Base:** `main` @ `31d7ffd` (A7 #140 + all D.5 work; pushed to both remotes)
|
||||
**Milestone:** M1.5 "Indoor world feels right" **Start with: #142 (issue #1).**
|
||||
**Predecessor:** `docs/research/2026-06-19-lighting-a7-fixD-round2-torch-reach-CHECKPOINT.md`
|
||||
(RESOLVED banner — the #140 outdoor fix). Companion: `claude-memory/reference_retail_ambient_values.md`.
|
||||
|
||||
## Where we are
|
||||
|
||||
`#140` (outdoor building over-bright near torches) is **SHIPPED + user-confirmed + merged + pushed.**
|
||||
Real cause: retail lights outdoor objects with SUN + ambient only, never torches (the `useSunlight`
|
||||
gate); fix = gate per-object torch selection on the object being indoor (`IndoorObjectReceivesTorches`,
|
||||
`WbDrawDispatcher.cs`). Register row **AP-43**.
|
||||
|
||||
At the #140 visual gate the user spotted two INDOOR-lighting gaps (the opposite problem — interiors
|
||||
too DARK / "like outdoors"). Both are this handoff. **Neither is a regression from #140** — that fix
|
||||
only *subtracts* torch light from *outdoor* objects.
|
||||
|
||||
## The unifying insight (read this first)
|
||||
|
||||
acdream's lighting **REGIME** (sun on/off + which ambient) is a **per-FRAME global** keyed on whether
|
||||
the PLAYER is in a sealed cell. Retail's is **per-DRAW-STAGE**: the outdoor stage runs with the sun
|
||||
on, the interior-cell stage runs with the sun off + torches on. `#140` fixed the **torch** half of
|
||||
this mismatch *per-object* (AP-43). **#142 is the SUN + AMBIENT half — i.e. the AP-43 residual, now
|
||||
surfaced as a visible bug.** Finishing #142 lets us delete/narrow AP-43.
|
||||
|
||||
---
|
||||
|
||||
# #142 (issue #1) — windowed-building interiors read "like outdoors" [PRIMARY]
|
||||
|
||||
### Symptom (user, 2026-06-19, at the #140 gate)
|
||||
> "Agent of Arcanum house — in retail it is much brighter indoors; when looking into the house it is
|
||||
> lit, same light when you walk in. In acdream it is NOT lit — looking in and when inside it feels the
|
||||
> same like it is outdoors."
|
||||
|
||||
The **meeting hall** (a more sealed interior) looked OK — the user only flagged its portal (#143),
|
||||
not its walls. That contrast is the key clue (see "the three gaps").
|
||||
|
||||
### Retail mechanism (VERIFIED — read verbatim this session)
|
||||
`PView::DrawCells` (0x005a4840) draws a frame in two ordered stages:
|
||||
1. **Outside stage:** `useSunlightSet(1)` (0x005a485a) → `LScape::draw` → outdoor terrain/buildings/
|
||||
objects, **sun on, torches skipped** (the #140 mechanism).
|
||||
2. **Interior stage:** `useSunlightSet(0)` (0x005a49f3) → `restore_all_lighting` → loop over **every**
|
||||
EnvCell in `cell_draw_list` → `DrawEnvCell` (0x0059f170): walls baked
|
||||
(`SetStaticLightingVertexColors` 0x0059cfe0), objects torch-lit (`minimize_object_lighting`
|
||||
0x0054d480, enabled because `useSunlight==0` per `DrawMeshInternal` 0x0059f398), **NO sun.**
|
||||
3. `useSunlightSet(1)` (0x005a4b5d) restores outdoor mode at the very end.
|
||||
|
||||
`useSunlightSet(arg)` (0x0054d450): sets `useSunlight=arg`; `arg==1` enables the SUN as the active
|
||||
hardware light, `arg==0` enables none (sun off).
|
||||
|
||||
**KEY FACT:** `cell_draw_list` holds ALL visible EnvCells — windowed (`SeenOutside`) **and** sealed.
|
||||
Retail draws every interior in the `useSunlight==0` stage. The regime is **per-stage, never per-
|
||||
building / per-SeenOutside.** So retail torch-lights *every* building interior, including windowed
|
||||
ones and look-ins viewed from outside.
|
||||
|
||||
### acdream current state (per-FRAME global) — current line refs (@31d7ffd)
|
||||
- `GameWindow.cs:8061` `playerSeenOutside = playerRoot?.SeenOutside ?? true` — the PLAYER cell's flag.
|
||||
- `GameWindow.cs:8107` `playerInsideCell = playerRoot is not null && !playerSeenOutside`.
|
||||
- `GameWindow.cs:8122` `UpdateSunFromSky(kf, playerInsideCell)` → (`:10786`) sets the **global** sun +
|
||||
ambient: inside → sun `Intensity=0` + flat `(0.2,0.2,0.2)` ambient; outside → keyframe sun + outdoor
|
||||
ambient.
|
||||
- That ambient is uploaded ONCE per frame to the SceneLighting UBO (`CurrentAmbient.AmbientColor`,
|
||||
`:8171`) and read by BOTH mode-0 (objects) and mode-1 (EnvCell shells) in `mesh_modern.vert`.
|
||||
- **Torches are ALREADY per-cell** (AP-43: `IndoorObjectReceivesTorches` `WbDrawDispatcher.cs:2076`,
|
||||
used at `:2057`; plus `EnvCellRenderer` `SelectForObject`) — independent of `playerInsideCell`. So
|
||||
the torch half is fine; **only the SUN + AMBIENT are still per-frame-global.**
|
||||
|
||||
### The three gaps (all one root: per-frame-global vs per-stage)
|
||||
1. **Player OUTSIDE, looking INTO any building (look-in):** `playerSeenOutside=true` → outdoor regime
|
||||
→ the look-in interior gets sun + outdoor ambient. Retail draws look-in cells in the `useSunlight=0`
|
||||
stage (torch-lit). → "when looking in, not lit."
|
||||
2. **Player INSIDE a WINDOWED building** (`SeenOutside=true` cells, e.g. Agent of Arcanum):
|
||||
`playerInsideCell=false` → outdoor regime → interior gets sun + outdoor ambient. Retail:
|
||||
`useSunlight=0`, torch-lit. → "when inside, feels like outdoors."
|
||||
3. **Player INSIDE a SEALED building / dungeon** (`SeenOutside=false`): `playerInsideCell=true` →
|
||||
indoor regime → MATCHES retail. ✓ (the meeting hall + dungeons — why they looked right.)
|
||||
|
||||
### Cheap validation FIRST (before any code)
|
||||
- **Confirm the windowed-vs-sealed split is the discriminator.** Verify the Agent of Arcanum is a
|
||||
WINDOWED building (its EnvCells' `SeenOutside=true`) and the meeting hall is sealed. Dat flag:
|
||||
`EnvCellFlags.SeenOutside` (hydrated to `ObjCell.SeenOutside`; see `EnvCell.cs` / `PhysicsDataCache.cs`).
|
||||
We did NOT pin the Agent of Arcanum's landblock this session — either have the user point at it in
|
||||
game (`[B.4b] pick` line names clicked objects), or extend `HoltburgTorchFalloffProbeTests` to dump
|
||||
`SeenOutside` per EnvCell across the Holtburg landblocks and find the windowed buildings.
|
||||
- **`ACDREAM_PROBE_LIGHT=1`** ([light] line logs `insideCell` / ambient / sun) while standing inside
|
||||
the Agent of Arcanum vs the meeting hall — confirms each gets the regime predicted above.
|
||||
|
||||
### Fix direction (BRAINSTORM this — it is a design fork, not a mechanical port)
|
||||
Make the SUN + AMBIENT **per-draw-context**, mirroring AP-43's per-object torch decision. The renderer
|
||||
is batched bindless-MDI, so a per-stage global won't work across mixed batches — per-object is the
|
||||
natural fit (exact same reasoning that put AP-43 per-object; see the #140 explanation). An object/cell
|
||||
is "indoor" iff its `ParentCellId` is an EnvCell (reuse `IndoorObjectReceivesTorches`). Then:
|
||||
- **Indoor draws** (mode-1 EnvCell shells; mode-0 objects with EnvCell `ParentCellId`): SKIP the sun +
|
||||
use the **indoor** ambient (flat `(0.2,0.2,0.2)` / retail indoor). (mode-1 already skips the sun;
|
||||
it just needs the indoor ambient. mode-0 indoor objects currently ADD the sun — gate it off.)
|
||||
- **Outdoor draws:** sun + outdoor ambient (as today).
|
||||
|
||||
Open design questions for the brainstorm:
|
||||
- The shader needs BOTH ambients (indoor + outdoor) + a per-instance "indoor" selector. Options:
|
||||
(a) add an `indoorAmbient` to the SceneLighting UBO + a per-instance indoor bit (a tiny SSBO like
|
||||
the light-set, or pack into an existing per-instance field); (b) add a third `uLightingMode` (e.g.
|
||||
`2 = indoor object`: no sun, indoor ambient, torches); (c) compute both and select.
|
||||
- `UpdateSunFromSky` must stop branching on `playerInsideCell` and instead provide BOTH regimes every
|
||||
frame (outdoor sun + outdoor ambient AND the indoor flat ambient), so the shader picks per object.
|
||||
- **Verify retail's indoor ambient** (the `restore_all_lighting` path + the per-EnvCell ambient): is it
|
||||
the flat `(0.2,0.2,0.2)` we use, or the cell's own authored ambient? Cross-check before locking it.
|
||||
|
||||
**This work RESOLVES the AP-43 residual** (regime becomes per-draw → no doorway/look-in mismatch).
|
||||
Update/delete AP-43 in the same commit.
|
||||
|
||||
### Files
|
||||
- `GameWindow.cs`: `:8061`/`:8107` (`playerInsideCell`), `:8122` + `:10786` `UpdateSunFromSky` (the
|
||||
regime source), `:8171` (ambient → UBO).
|
||||
- `src/AcDream.App/Rendering/Shaders/mesh_modern.vert`: `accumulateLights` (sun loop under
|
||||
`if (uLightingMode==0)` ~`:193`; ambient `uCellAmbient.xyz` ~`:188`). The sun gate + ambient
|
||||
selection live here.
|
||||
- `WbDrawDispatcher.cs`: `IndoorObjectReceivesTorches` (`:2076`) — the indoor predicate to reuse;
|
||||
`ComputeEntityLightSet` (`:2057`).
|
||||
- `EnvCellRenderer.cs`: mode-1 draws (`uLightingMode=1`) — need the indoor ambient.
|
||||
- `LightManager` / the SceneLighting UBO layout (`GlobalLightPacker` is the binding-4 helper) — where a
|
||||
second ambient + the indoor selector would go.
|
||||
|
||||
---
|
||||
|
||||
# #143 (issue #2) — portal swirl doesn't light the room [SECONDARY]
|
||||
|
||||
### Symptom
|
||||
Inside the meeting hall, retail's portal swirl visibly tints/lights the room; acdream's portal lights
|
||||
nothing.
|
||||
|
||||
### Retail mechanism
|
||||
The portal swirl is a **DYNAMIC** light. `add_dynamic_light` (0x0054d420) → `insert_light`
|
||||
(0x0054d1b0) → `world_lights.dynamic_lights`. `minimize_envcell_lighting` (0x0054c170) enables the
|
||||
cell's DYNAMIC subset (class 2) as hardware lights → tints the EnvCell walls; `minimize_object_lighting`
|
||||
(0x0054d480) enables dynamics for objects in the cell too. **Captured params** (predecessor cdb,
|
||||
`tools/cdb/a7-fixd-*.cdb`): the Holtburg portal dynamic light = `intensity=100, falloff=6,
|
||||
color=(0.784, 0, 0.784)` (magenta/purple).
|
||||
|
||||
### acdream gap
|
||||
acdream registers ONLY static `Setup.Lights` (`GameWindow.cs` ~`:6404` `RegisterOwnedLight`). It
|
||||
registers **no dynamic lights** — the portal entity casts no light. (`GpuWorldState.cs:101` even
|
||||
mentions "unregistering dynamic lights" but none are ever registered.)
|
||||
|
||||
### Fix approach
|
||||
Register a dynamic `LightSource` for portal-swirl entities at their world position with the retail
|
||||
params (or read the portal model's own dat `Setup.Lights` if it carries one — check the portal GfxObj/
|
||||
Setup first). It then flows through the existing point-light path (`LightManager.PointSnapshot` →
|
||||
`SelectForObject` → shader), lighting nearby EnvCell walls + indoor objects. It is a POINT light, lives
|
||||
INSIDE a cell → it must light via the indoor path (the EnvCell bake `SelectForObject` already picks any
|
||||
registered point light near a cell, so registering it may "just work" once it has a `LightSource`).
|
||||
Find where portal swirls spawn in acdream (the particle/portal emitter spawn path) and attach the light
|
||||
there; unregister on despawn (`UnregisterByOwner`). Keep it OUT of the AP-43 outdoor-object gate (it's
|
||||
indoor). Decomp anchors: `add_dynamic_light` 0x0054d420, `minimize_envcell_lighting` 0x0054c170,
|
||||
`insert_light` 0x0054d1b0.
|
||||
|
||||
---
|
||||
|
||||
## Decomp anchors (quick reference)
|
||||
`useSunlightSet` 0x0054d450 · `useSunlight` gate `DrawMeshInternal` 0x0059f398 · `PView::DrawCells`
|
||||
0x005a4840 (`useSunlightSet(1)` 0x005a485a / `useSunlightSet(0)` 0x005a49f3 / `useSunlightSet(1)`
|
||||
0x005a4b5d) · `DrawEnvCell` 0x0059f170 · `SetStaticLightingVertexColors` 0x0059cfe0 · `calc_point_light`
|
||||
0x0059c8b0 (range = falloff × `static_light_factor` 1.3 @ 0x00820e24) · `minimize_object_lighting`
|
||||
0x0054d480 · `minimize_envcell_lighting` 0x0054c170 · `add_dynamic_light` 0x0054d420 · `insert_light`
|
||||
0x0054d1b0 · `config_hardware_light` 0x0059ad30 (`rangeAdjust` 1.5 @ 0x00820cc4 — the dynamic/object
|
||||
hardware path).
|
||||
|
||||
## DO-NOT-RETRY / gotchas
|
||||
- The OUTDOOR torch gate (#140 / AP-43) is correct + user-confirmed — don't touch it.
|
||||
- Don't shorten `Falloff × 1.3` — acdream reads the dat falloffs faithfully (the reach is correct).
|
||||
- The regime is a per-FRAME global; the fix is to make sun+ambient **per-DRAW** (per-object/cell),
|
||||
mirroring AP-43's torch decision — **NOT** to split into separate render passes (fights the batched
|
||||
MDI; the per-object route is why AP-43 exists).
|
||||
- Line numbers above are @`31d7ffd` and WILL drift — re-grep `playerInsideCell` / `UpdateSunFromSky` /
|
||||
`IndoorObjectReceivesTorches` before editing.
|
||||
|
||||
## Verification (the acceptance gate)
|
||||
Visual side-by-side vs retail at the **Agent of Arcanum** (looking IN from outside + walking IN) and
|
||||
the **meeting-hall portal**. Expected after #142: interiors are torch-lit/warm both looking-in and
|
||||
inside; windowed buildings no longer "feel like outdoors." After #143: the portal swirl tints the room.
|
||||
|
||||
## Pointers
|
||||
- Register: **AP-43** (`docs/architecture/retail-divergence-register.md`) — the residual this work
|
||||
resolves.
|
||||
- `claude-memory/reference_retail_ambient_values.md` — cdb values incl. the portal dynamic-light
|
||||
capture + the indoor/outdoor ambient numbers.
|
||||
- `claude-memory/project_render_pipeline_digest.md` — per-cell light + look-in (#124) + flap context.
|
||||
- #140 CHECKPOINT (above) — the full outdoor-torch story + the verified `useSunlight` decomp.
|
||||
Loading…
Add table
Add a link
Reference in a new issue