# Dereth celestial shadow sources **Date:** 2026-08-22 **Status:** measured retail-DAT and named-retail finding; implementation input for Campaign AR **Scope:** identify the Dereth sun/moons and define the opt-in pack's dominant directional-shadow source. This note does not change the retail rendering path. ## Conclusion Dereth's Region `0x13000000` consistently authors three moving celestial meshes across all 20 day groups: 1. `0x01001348` is the sun disk. 2. `0x01001F6A` is the large, haloed moon and is the dominant lunar source. 3. `0x01001F67` is the smaller secondary moon. Retail does **not** provide a separate lighting colour or intensity for each mesh. `SkyDesc::GetLighting` produces one interpolated directional vector, colour, and brightness from `SkyTimeOfDay.DirHeading`, `DirPitch`, `DirColor`, and `DirBright`. The opt-in atmospheric pack therefore uses the selected visible celestial mesh only for shadow **direction**. Colour and energy remain the single AC-authored directional-light values. The deterministic priority is: 1. visible sun whose transformed centre is above the horizon; 2. visible large/haloed moon whose transformed centre is above the horizon; 3. visible secondary moon whose transformed centre is above the horizon; 4. no directional shadow source. This is a pack enhancement, not a claim that retail cast real-time moon shadows. ## Evidence and provenance The investigation followed the project rendering inventory and used the already-loaded retail structures rather than inventing another sky model. Evidence came from: - `artifacts/atmospheric-rendering/sky-heading-dump/client.log`, especially lines 40-88 for Sunny day group 0 and the corresponding repeated entries for all later day groups. The dump records the three IDs, visibility windows, angular sweeps, keyframe directional lighting, and the sun surface. - A read-only `DatCollection.Get`/`Get` probe against the installed Asheron's Call DATs, using the same inspection path implemented by `tools/SkyObjectInspect/Program.cs`, for all three `GfxObj` sort centres, polygon geometry, surfaces, and texture chains. - `tools/RainMeshProbe/Program.cs` lines 37-49, which names and audits the celestial surface set independently of the shadow implementation. - `docs/research/named-retail/acclient_2013_pseudo_c.txt`: `SkyDesc::GetLighting` at `0x00500a80` (around line 261291), `SkyDesc::GetSky` at `0x00501ec0` (around line 262761), `GameSky::CalcFrame` at `0x00506f80` (around line 268650), and `GameSky::UseTime` at `0x005075b0` (around line 269090). - `docs/research/2026-04-23-sky-retail-verbatim.md`, especially its recorded directional-light interpolation and `GameSky::UseTime` material updates. No fresh decompilation was required. The named-retail corpus already answered the only question the current code and DAT dump could not answer on their own: whether a moon mesh contributes a second retail world light. It does not. ## Installed-DAT characterization The following values were read from the installed Dereth Region and the three referenced `GfxObj`/surface/texture chains. The same three object IDs, windows, and sweeps occur in every one of the 20 day groups; only their object index changes between seven-object and weather-heavy groups. | Role | GfxObj | Day window | Angular sweep | Authored `SortCenter` | |---|---:|---:|---:|---:| | Sun disk | `0x01001348` | `0.1600..0.9400` | `-23 deg..203 deg` | `(1050, 0, 0)` | | Secondary moon | `0x01001F67` | `0.0400..0.2100` | `-20 deg..190 deg` | `(1909.46, 1874.78, -0.0000157485)` | | Dominant moon + halo | `0x01001F6A` | `0.0000..0.2300` | `-20 deg..190 deg` | `(2066.82, 552.99, 0)` | The asset chain establishes the visual identities and the dominant-moon choice: | GfxObj | Surface | Surface flags | SurfaceTexture | RenderSurface | Image | |---:|---:|---|---:|---:|---| | `0x01001348` | `0x080000D1` | Base1Image, Alpha, Additive | `0x050014CD` | `0x0600388D` | 128x128 `PFID_R8G8B8` sun disk | | `0x01001F67` | `0x080000D2` | Base1ClipMap | `0x05001A6C` | `0x06003894` | 256x256 `PFID_INDEX16`, palette `0x0400103F` | | `0x01001F6A` | `0x080000D6` | Base1ClipMap | `0x05001A6D` | `0x06003898` | 256x256 `PFID_INDEX16`, palette `0x0400103F` | | `0x01001F6A` | `0x080000D7` | Base1Image, Alpha, Additive | `0x05001A6E` | `0x06003899` | 128x128 `PFID_R8G8B8` halo | Every listed surface has authored `Luminosity=1`, `Diffuse=1`, and `Translucency=0`. The large moon's primary quad has roughly 2.3 times the polygon area of the secondary moon before its still larger additive halo is counted. That makes `0x01001F6A` the unambiguous dominant lunar visual when both moons are above the horizon. These installed-DAT facts are characterization evidence, not an ordinary test dependency. Unit tests use hand-built `DayGroupData` so clean CI and machines without retail DATs remain deterministic. ## Direction and visibility contract `SkyObjectData.IsVisible(dayFraction)` owns the normal, always-visible, and midnight-wrapping window cases. `CurrentAngle(dayFraction)` owns the authored arc interpolation, including progress through a wrapping window. The selected direction must match the sky renderer exactly: ```text heading = active SkyObjectReplace.Rotate arc = SkyObjectData.CurrentAngle(dayFraction) model = RotationZ(-heading) * RotationY(-arc) anchor = effective GfxObj.SortCenter direction = normalize(TransformNormal(anchor, model)) ``` “Effective” means that an active non-zero replacement `GfxObjId` also supplies its own `SortCenter`. A replacement with `Transparent >= 1` makes the object ineligible. The replacement lookup follows the renderer's discrete active keyframe rule; it does not interpolate replacement fields. A zero, non-finite, or below/on-horizon transformed direction is ineligible. This deliberately does not substitute `SkyTimeOfDay.DirHeading/DirPitch` for moon direction. Those values are the one retail world-light direction. The moon meshes have separate authored arcs, and the enhancement is specifically intended to align moon shadows with the moon the player can see. ## Authored light contribution Named retail `SkyDesc::GetLighting` interpolates the two surrounding `SkyTimeOfDay` records and produces: ```text sunVector = DirBright * ( cos(DirPitch) * sin(DirHeading), cos(DirPitch) * cos(DirHeading), sin(DirPitch)) directionalColor = DirColor * length(sunVector) ``` `length(sunVector)` is `DirBright`. acdream exposes the resulting colour as `SkyKeyframe.SunColor`. The pack's scalar authored energy is therefore `clamp(max(SunColor.r, SunColor.g, SunColor.b), 0, 1)`. By contrast, named retail `GameSky::UseTime` sends a celestial replacement's `Luminosity`, `MaxBright`, and `Transparent` to the mesh material through `SetLuminosity`, `SetDiffusion`, and `SetTranslucency`. It does not install a second directional light. Texture brightness and moon surface luminosity must not manufacture extra world-light energy. Weather/day-group reductions, softness, and elevation ramps remain explicit render-pack policy. They are not mislabelled as measured retail intensities. ## Parity and safety registration ### Retail behavior - One interpolated directional world-light channel comes from `SkyTimeOfDay.Dir*`. - Celestial meshes follow their own visibility windows and transformed arcs. - Replacement luminosity/diffusion/transparency changes mesh material state, not the number of world-directional lights. - Retail does not render the Campaign AR cascaded real-time object shadows. ### Opt-in pack enhancement - The pack chooses the visible sun or dominant visible moon direction for its directional shadow map. - Moon direction follows the rendered moon; energy remains the single AC-authored directional channel. - Sun wins any overlap when its transformed centre is above the horizon; otherwise the haloed moon wins before the secondary moon. - This deviation belongs in the atmospheric render-pack entry of `docs/architecture/retail-divergence-register.md`. ### Unchanged boundaries - The retail rendering path remains the default and authoritative output. - Pack-off frames do not resolve or render celestial shadow work. - Existing retail scene lighting remains driven by `SkyStateProvider`; this policy does not replace it. - Physics, collision, containment, selection, movement, and DAT geometry are untouched. The selected source is an immutable one-frame rendering fact. ## Deterministic acceptance coverage `tests/AcDream.App.Tests/Rendering/Packs/AuthoredCelestialShadowSourceResolverTests.cs` locks: - the three verified IDs and priority independent of object-list order; - sun overlap, dominant-moon fallback, and secondary-moon fallback; - fully transparent and effective replacement behavior; - replacement rotation and the exact renderer transform direction; - no-visible/no-above-horizon suppression; - midnight-wrapping visibility and angle progress; and - directional colour-times-brightness energy, including preservation when no celestial source is available. The test fixture is entirely hand-built. It neither requires nor silently substitutes installed retail DAT content. The complete non-physical verification result, including shader ABI, exact retail-binary preservation, performance/lifetime fixtures, locked restore, Release build, and fresh-process totals, is recorded in the [Campaign AR Stage 1 automated gate report](2026-08-22-atmospheric-stage1-automated-gate.md).