Session handoff: live-cdb grounding shipped Fix A (point-light shape), Fix B (per-object selection), Fix C (sun-vector magnitude / ~32% over-bright). Fix D (outdoor objects too bright near torches) is fully grounded but BLOCKED on one capture (the building's render path) — the D3D-FF math says it'd make objects brighter, so not ported. Full cdb cheat-sheet + the contradiction + the next capture in the doc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
114 lines
7.3 KiB
Markdown
114 lines
7.3 KiB
Markdown
# A7 Lighting — Fix A/B/C SHIPPED, Fix D (object torch over-brightness) HANDOFF
|
||
|
||
**Date:** 2026-06-18 **Branch:** claude/thirsty-goldberg-51bb9b (merged to main)
|
||
**Companion memory:** `claude-memory/reference_retail_ambient_values.md` (all captured
|
||
values + cdb recipes) and `reference_retail_chat_colors.md` (cdb method).
|
||
|
||
This session made acdream's outdoor + ambient lighting retail-faithful by grounding
|
||
everything in **live cdb on the retail client** (no guessing). Three fixes shipped;
|
||
a fourth (Fix D — outdoor objects too bright near torches) is fully grounded but
|
||
**deliberately NOT implemented** because the math contradicts the observed result —
|
||
one more capture is needed first.
|
||
|
||
## SHIPPED this session (all on `main`)
|
||
|
||
| Fix | Commit | What | Result |
|
||
|---|---|---|---|
|
||
| **A** | `aa94ced` | point-light SHAPE: per-vertex Gouraud + faithful `calc_point_light` (wrap + norm), per-channel cap | killed the "spotlight" disc — user "way better" |
|
||
| **B** | `4345e77` | per-OBJECT light selection (`minimize_object_lighting`): each object picks its own ≤8 lights by its AABB sphere, camera-independent | killed "building lights up as you approach"; a Holtburg view has **129** point lights vs the old global cap of 8 |
|
||
| **C** | `57c1135` | sun-vector magnitude: ambient + sun were **~32% too bright** | ambient now matches retail within ~2%; user "general ambient better outside" |
|
||
|
||
**Fix B mechanism** (for context): two new SSBOs in `mesh_modern.vert` — binding=4
|
||
GLOBAL light array (`LightManager.PointSnapshot`), binding=5 per-instance 8-int
|
||
light set (mirrors the U.3 clip-slot SSBO). `LightManager.SelectForObject` +
|
||
`BuildPointLightSnapshot` (pure, TDD). `WbDrawDispatcher` computes each entity's
|
||
light set once per entity (like `_currentEntitySlot`), threads it parallel to the
|
||
matrices.
|
||
|
||
**Fix C mechanism:** `SkyStateProvider.RetailSunVector` had `y = cos(P)` (≈1) — the
|
||
PRE-transform value `SkyDesc::GetLighting` writes to its arg5 (0x00500ac9), before
|
||
`LScape::set_sky_position`'s world transform. cdb read retail's actual
|
||
`LScape::sunlight = (0.2238, ~0, 0.00352)`, magnitude = DirBright. Corrected to the
|
||
world-space spherical form `DirBright × (cos P·sin H, cos P·cos H, sin P)`,
|
||
`|sunVec| == DirBright`. Feeds BOTH the ambient boost AND the sun colour, so it
|
||
dims **terrain + objects + sky** (all read the shared SceneLighting UBO). 18/18 sky
|
||
tests green (old tests pinned the inflated magnitude — updated to cdb-verified).
|
||
|
||
## KEY LESSON: the "too purple" was NEVER a bug
|
||
|
||
The user's side-by-side ("acdream too purple, retail neutral") was a comparison
|
||
**across different times of day**. Live cdb at the SAME game time + DayGroup proved
|
||
acdream's time, weather (DayGroup selection), AND ambient COLOR all match retail
|
||
exactly — the purple `AmbColor=(200,100,255)` is authored per-time-of-day in the
|
||
sky dat (twilight = purple, midday = neutral `(230,230,255)`). Only the *brightness*
|
||
was wrong (Fix C). Don't re-investigate the purple.
|
||
|
||
---
|
||
|
||
## OPEN — Fix D: outdoor OBJECTS too bright near torches
|
||
|
||
**Symptom (user, 2026-06-18):** the Holtburg meeting-hall walls blow out warm/bright
|
||
in acdream vs dim in retail. Fix A/B/C did NOT touch this. It's the per-object
|
||
point-light **contribution on objects**.
|
||
|
||
### Grounded (cdb + decomp) — retail's object point-light path
|
||
`Render::config_hardware_light` (0x0059ad30) builds the `D3DLIGHT9`:
|
||
- `Diffuse = color × intensity`
|
||
- `Attenuation = (0, 1, 0)` ⇒ **1/d** (inverse-LINEAR; acdream's `calc_point_light`
|
||
is `~1/d²` via norm = distsq·d)
|
||
- `Range = falloff × rangeAdjust`, **`rangeAdjust = 1.5`** (0x00820cc4) ⇒ torch Range
|
||
= 6×1.5 = **9 m** (LARGER than acdream's falloff×1.3 = 7.8 m — range is NOT why
|
||
we're brighter)
|
||
- live `LIGHTINFO` captured: torch `type=0 intensity=100 falloff=6`; a 2nd light
|
||
`intensity=2.25 falloff=10`
|
||
- `d3d_material.Diffuse = (1,1,1)` white (decomp 0x00539774)
|
||
|
||
### THE CONTRADICTION (resolve this FIRST next session)
|
||
By `mat(1)×color×100×(N·L)×(1/d)`, a torch 3 m away = `color×33` ⇒ retail's walls
|
||
SHOULD blow to **WHITE** — but they're **DIM**. Material diffuse, range, and
|
||
intensity are all captured and ruled out. So the scaling lives in the building's
|
||
**RENDER PATH**, which is unknown. **⚠ DO NOT port the D3D-FF model — by this math it
|
||
would make objects BRIGHTER (white), the opposite of the fix.**
|
||
|
||
### The decisive next capture
|
||
Determine the static building's ACTUAL render path:
|
||
- **Hypothesis (a) — MOST LIKELY:** static buildings DON'T use D3D hardware lighting.
|
||
They use the `D3DPolyRender::SetStaticLightingVertexColors` BAKE (0x0059cfe0 →
|
||
`calc_point_light`), like EnvCells. The `config_hardware_light` lights I captured
|
||
were for a DIFFERENT object (player / creature / the purple PORTAL — note the
|
||
`intensity=100` could be the portal, not the wall torch). If (a) holds, acdream's
|
||
`calc_point_light` is the RIGHT model and the over-brightness is the **per-channel
|
||
cap** (`min(scale×col,col)` lets several torches each reach full colour and sum to
|
||
white) and/or **too many torches selected** per object and/or a missing clamp step.
|
||
- **Hypothesis (b):** `D3DRS_LIGHTING` off / lights not `LightEnable`'d for the
|
||
building draw.
|
||
- **How to capture:** break at `SetStaticLightingVertexColors` (0x0059cfe0) and see
|
||
whether it's called for the building's mesh (confirms the bake path); and/or
|
||
inspect the render state around the static-object `DrawIndexedPrimitive`
|
||
(`D3DRS_LIGHTING`, which lights are enabled). Also: at `config_hardware_light`,
|
||
dump WHICH object/owner the light is being configured for to identify whether the
|
||
`intensity=100` light is the torch or the portal.
|
||
|
||
### acdream side — where the fix lands
|
||
- acdream runs `calc_point_light` (wrap/norm + per-channel cap) for ALL meshes via
|
||
`mesh_modern.vert` `pointContribution` (objects AND cells — Fix A).
|
||
- If buildings use the bake, the likely fix is in the **cap / sum / count**, not the
|
||
attenuation model. Files: `src/AcDream.App/Rendering/Shaders/mesh_modern.vert`
|
||
(`pointContribution` + `accumulateLights`), `src/AcDream.Core/Lighting/LightManager.cs`
|
||
(`SelectForObject`), `LightBake.cs` (verbatim calc_point_light, still unwired).
|
||
|
||
---
|
||
|
||
## cdb cheat-sheet (all verified this session; binary MATCHES refs/acclient.pdb)
|
||
- `bp acclient!SmartBox::SetWorldAmbientLight` (0x004530a0) — arg2=level `[esp+4]`, arg3=color32 `[esp+8]`
|
||
- `bp acclient!SkyDesc::GetLighting` (0x00500a80) — arg2=dayFraction `[esp+4]`; `dt acclient!SkyDesc @ecx present_day_group`
|
||
- `LScape::sunlight` global @ **0x00841940** (Vector3); `LScape::ambient_level` @ 0x00841770
|
||
- `bp acclient!PrimD3DRender::config_hardware_light` (0x0059ad30) — arg4=LIGHTINFO `[esp+0x10]`; `dt acclient!LIGHTINFO dwo(@esp+0x10) type intensity falloff color`
|
||
- `rangeAdjust = 1.5` @ 0x00820cc4; `D3DPolyRender::SetStaticLightingVertexColors` @ 0x0059cfe0
|
||
- Pattern: `.formats poi(<addr>)` for floats, `dwo(<addr>)` for dwords, `qd` after N hits to auto-detach (keeps retail alive). User must have retail in-world first.
|
||
- acdream probes: `ACDREAM_PROBE_LIGHT=1` (`[light]` ambient+sun line), `ACDREAM_DUMP_SKY=1` (keyframes + dayFraction + DayGroup).
|
||
|
||
## Build / run
|
||
`dotnet build src/AcDream.App/AcDream.App.csproj -c Debug` (green). Standard
|
||
`ACDREAM_LIVE` launch env in CLAUDE.md. Close the client before rebuilding (it locks
|
||
the DLLs). 18/18 sky tests + 17/17 LightManager + 36/36 dispatcher clip-slot green.
|