200 lines
9.3 KiB
Markdown
200 lines
9.3 KiB
Markdown
# 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<GfxObj>`/`Get<Surface>` 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).
|