9.3 KiB
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:
0x01001348is the sun disk.0x01001F6Ais the large, haloed moon and is the dominant lunar source.0x01001F67is 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:
- visible sun whose transformed centre is above the horizon;
- visible large/haloed moon whose transformed centre is above the horizon;
- visible secondary moon whose transformed centre is above the horizon;
- 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 bytools/SkyObjectInspect/Program.cs, for all threeGfxObjsort centres, polygon geometry, surfaces, and texture chains. tools/RainMeshProbe/Program.cslines 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::GetLightingat0x00500a80(around line 261291),SkyDesc::GetSkyat0x00501ec0(around line 262761),GameSky::CalcFrameat0x00506f80(around line 268650), andGameSky::UseTimeat0x005075b0(around line 269090).docs/research/2026-04-23-sky-retail-verbatim.md, especially its recorded directional-light interpolation andGameSky::UseTimematerial 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:
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:
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.