Modern open-source C# .NET 10 Asheron's Call client. Faithful port of retail client behaviour to Silk.NET with a plugin API.
Find a file
Erik aa2e20a42e sky(phase-2): retail-verbatim per-vertex lighting via Surface.Luminosity
Phase 2 of the sky port. Empirically confirmed from the Phase 1 dump
(ACDREAM_DUMP_SKY=1 on the live Dereth region): retail distinguishes
self-illuminated sky meshes from lit ones by the `Surface.Luminosity`
FLOAT field (0..1), NOT by the `SurfaceType.Luminous` flag bit (none of
Dereth's sky meshes have the flag set).

Observed values on the 4 currently-visible sky GfxObjs:

  GfxObj 0x010015EE (dome, 4 surfaces)    Luminosity = 1.0
  GfxObj 0x010015EF (upper cloud)         Luminosity = 0.0
  GfxObj 0x01004C36 (lower drift cloud)   Luminosity = 0.0
  GfxObj 0x01001348 (sun/moon additive)   Luminosity = 1.0

Retail uses this as an emissive coefficient in the per-vertex lighting
formula (decompiled chunk_00500000.c:7535 FUN_00508010 + chunk_00530000.c
AdjustPlanes per-vertex math):

  tint = clamp(vec3(Luminosity) + AmbColor*AmbBright
               + max(dot(N, -sunDir), 0) * DirColor*DirBright,
               0.0, 1.0)
  fragment = texture * tint

When Luminosity=1.0 the clamp saturates → full texture brightness
regardless of time of day (dome gradient preserved; sun/moon always
bright). When Luminosity=0.0 only the ambient + diffuse term drives the
tint, so clouds pick up the time-of-day ambient (purple at midnight
per AmbColor=(200,100,255)×AmbBright=0.4 ≈ (0.31,0.16,0.40); warm tan
at dusk; pale-cool at noon).

Also empirically confirmed: raw SkyObjectReplace Transparent/Luminosity
/MaxBright are in 0..100 percent range (observed 11, 15, 22, 66, 100,
and -1 sentinel). The `/100` divide in SkyDescLoader (eeae83a) is
retail-correct; `_DAT_007a1870` in the decompile must be 0.01f.

Code changes:
- src/AcDream.Core/Meshing/GfxObjSubMesh.cs: new `Luminosity` field on
  the per-submesh record (0..1, defaults to 0 for non-sky meshes).
- src/AcDream.Core/Meshing/GfxObjMesh.cs: pull Surface.Luminosity when
  building submeshes (alongside existing Translucency capture).
- src/AcDream.App/Rendering/Sky/SkyRenderer.cs:
  - SubMeshGpu gains SurfLuminosity, propagated from GfxObjSubMesh.
  - Render() pushes uAmbientColor/uSunColor/uSunDir once per frame from
    the interpolated keyframe; uEmissive once per submesh.
  - uTint uniform removed (replaced by the vTint varying computed in
    the vertex shader).
- src/AcDream.App/Rendering/Shaders/sky.vert: computes vTint per-vertex
  using the retail AdjustPlanes formula.
- src/AcDream.App/Rendering/Shaders/sky.frag: consumes vTint, drops
  uTint uniform. uLuminosity (the per-keyframe SkyObjectReplace
  override) still applied as a final scalar multiply.

Expected visual difference from Phase 1 baseline:
  - Dome gradient: IDENTICAL (Luminosity=1 saturates).
  - Sun / moon: IDENTICAL (Luminosity=1 saturates, additive blend).
  - Clouds: now tinted by time of day. Midnight → purple haze. Noon →
    pale cool. Dusk → warm tan.

Open questions (unchanged from Phase 1 doc):
  - Does the 15s LightTickSize throttling need porting? Phase 3.
  - Does FUN_00532440 (AdjustPlanes per-cell terrain relight) need
    porting for non-sky geometry to follow the sky? Phase 3.

Build + 717 tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 18:19:22 +02:00
docs sky(phase-1): revert speculative tint, add ACDREAM_DUMP_SKY diagnostic 2026-04-23 18:06:52 +02:00
memory docs(memory): session 2026-04-19 handoff — remote motion port complete 2026-04-19 21:33:12 +02:00
src sky(phase-2): retail-verbatim per-vertex lighting via Surface.Luminosity 2026-04-23 18:19:22 +02:00
tests fix(terrain): align per-cell triangle geometry with ACE's ConstructPolygons convention 2026-04-21 13:20:59 +02:00
tools feat(core): port decompiled AC client physics — CollisionPrimitives + PhysicsBody 2026-04-12 23:54:51 +02:00
.gitignore chore: gitignore launch.log 2026-04-18 23:11:56 +02:00
AcDream.slnx feat(net): AcDream.Core.Net scaffold + ISAAC keystream (Phase 4.1) 2026-04-11 14:14:28 +02:00
CLAUDE.md docs(claude.md): add 'Running the client against the live server' section 2026-04-19 14:58:13 +02:00
README.md chore: phase 0 — skeleton + dat asset inventory 2026-04-10 09:02:56 +02:00

acdream

Experimental modern open-source Asheron's Call client in C# / .NET 10.

Status: pre-alpha, not playable. Phase 0 only — dat file asset inventory.

Stack: .NET 10, Chorizite.DatReaderWriter for dat parsing. Silk.NET + Avalonia planned for rendering/UI (not yet wired up).

Requires: A retail Asheron's Call install (Turbine/Microsoft property — supply your own). Set ACDREAM_DAT_DIR environment variable to the directory containing client_portal.dat, client_cell_1.dat, client_highres.dat, and client_local_English.dat, or pass it as the first CLI argument.

Layout

  • src/AcDream.Cli/ — console app that dumps asset counts from a dat directory
  • references/ — local read-only reference material (ACE, ACViewer, WorldBuilder, DatReaderWriter, holtburger, retail AC install). Gitignored.

Run

dotnet run --project src/AcDream.Cli -- "C:\path\to\Asheron's Call"

Or set ACDREAM_DAT_DIR and run without args.