Captures everything learned from a long worktree iteration on the foreground-rain bug (ISSUES.md #1 / #26) plus a new star-rendering bug observed in the same area. The code work from that worktree (WeatherDispatcher, EmitterDescLoader.LoadFromDat, WeatherCellRenderer, GameWindow integration) was reverted because it didn't visibly fix the rain bug — but the research findings + diagnostic tools are durable and should not have to be rediscovered. What's added: - docs/research/2026-04-26-sky-investigation-handoff.md Comprehensive seed prompt for the next session. Covers: * Bug A: foreground rain (#26) — what's open, what's confirmed, what's been tried * Bug B: stars rendering as square in corner (NEW, user-observed) * 40-agent decomp scan findings — retail rain is NOT camera- particles, NOT server-driven, NOT screen-space; the mesh IS a hollow octagonal tube; only 5 weather GfxObjs in Dereth * Things ruled out by trial (envelope, scaling, unlit, depth- always alone, Setup loading) * Things to try next (depth+zfar combined, full render-state audit, frame ordering, star UV bug as easier first target) * Acceptance criteria for "done" - docs/research/2026-04-26-chorizite-pr-draft.md Upstream PR draft for Chorizite/DatReaderWriter. Five generated DBObj source files reference nonexistent enum values and are silently excluded from the NuGet build: ParticleEmitterInfo, Clothing, PaletteSet, DataIdMapper, DualDataIdMapper. Fix: delete the duplicates. Independent of the rain work — benefits the AC modding ecosystem broadly. - docs/research/2026-04-26-datreaderwriter-reference.md Developer reference for our DatReaderWriter usage. Version, types we consume, known broken types, thread-safety caveats, upgrade procedure, NuGet-vs-vendored decision matrix. - tools/PesChainAudit/ Recursive PES walker — given a 0x33xxxxxx script id, walks all CallPES references and dumps every hook + every referenced ParticleEmitter's parameters. Used to prove no weather PES emits rain particles. - tools/TextureDump/ Dumps texture pixel statistics (alpha histogram, brightness, max) and saves as PNG for visual inspection. - tools/WeatherEnumerator/ Enumerates every DayGroup in a Region, lists weather SkyObjects (Properties & 0x04), dumps GfxObj bounding boxes. - tools/WeatherSetupProbe/ Loads a Setup id, dumps each part's GfxObj + frame + scale + surface. Used to prove weather Setups are 5cm dummy carriers. Worktree feature/sky-fixes is being deleted in a follow-up step. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 KiB
Sky / Weather Investigation — Handoff for Next Session
Date: 2026-04-26 Author: Claude (Opus 4.7) closing out a long worktree Purpose: seed prompt for a fresh session that resumes investigation of two sky-related visual bugs without re-deriving everything we learned the hard way.
What you're picking up
Two open visual bugs in the sky/weather pipeline, both observed against a live ACE server in Holtburg:
Bug A — Foreground rain missing (ISSUES.md #26)
In retail, when the active DayGroup is "Rainy", you see rain everywhere — in the air between the camera and your character, on the horizon, overhead. Volumetric appearance.
In acdream as of main (post-revert), you see no falling rain at all. Even the previous (rolled-back) work that rendered the rain mesh in world space produced only a "discrete cylinder of rain" around the player, not the volumetric look retail has.
Bug B — Stars appear as a square in one corner instead of stretched across the sky
User-observed during a recent night-time launch. The star sky-object's texture appears as a small square in one corner of the geometry instead of being mapped across the full sky dome / star plane. Suspected: UV-mapping bug, or wrong texture wrap mode (CLAMP vs REPEAT), or wrong geometry for the star object.
Both bugs may share a root cause in how we handle sky GfxObj UVs / texture sampling. Worth investigating B first because it might be simpler AND might reveal something that explains A too.
What's on main right now
- The original
SkyRenderer(camera-anchored sky pass) — unchanged. - The original hardcoded camera-attached rain emitter (
BuildRainDesc/BuildSnowDesc+UpdateWeatherParticles) inGameWindow.cs— still uses the brokenStartAlpha = 0.3 → EndAlpha = 0linear fade. This is the alpha-fade bug from the original Issue #1 (rain fades to invisible exactly at ground level). - ISSUES.md #1 is OPEN and #26 hasn't been filed yet on main (it lived only on the deleted
feature/sky-fixesbranch).
This handoff doc captures everything #26 contained, so don't worry about losing it.
What we already learned (40-agent decomp scan, 2026-04-26)
Confirmed truths about retail rain:
-
Retail rain is NOT camera-attached particles. Across the entire 1.4M-line decomp at
docs/research/named-retail/acclient_2013_pseudo_c.txt, only TWO callers ofParticleManager::CreateParticleEmitterexist — both are animation-hook executors (CreateParticleHook::Execute,CreateBlockingParticleHook::Execute). No camera-attached rain spawner exists in retail. -
Retail rain is NOT server-driven. No inbound network message triggers rain. ACE doesn't broadcast it. Weather is entirely client-side, dat-driven from
Region.SkyInfo.DayGroups[i].SkyObjects[].EnvironChangeType(fog colors + 6 thunder sound enums) is the only weather-related broadcast and it doesn't trigger visuals. -
Retail rain is NOT screen-space. No fullscreen quad, no orthographic projection, no render-target swap, no post-process. Verified via grep for
D3DXMatrixOrtho,SetRenderTarget,FullscreenQuad, etc. -
The mesh IS exactly a hollow octagonal tube. Vertex dump of
0x01004C42and0x01004C44:- 24 verts (some duplicated for UV seams)
- 16 triangles forming 8 vertical wall quads
- Octagonal corners at radius 113m around center
- 815m tall (z=0.11 to z=814.90)
- No top/bottom caps — purely walls
- Surface 0x080000C5 =
Base1Image | Translucent | Alpha | Additive, SurfTranslucency=0.5
-
Only 5 unique weather GfxObjs exist in all of Dereth's 20 DayGroups:
0x01004C44— hollow octagonal tube, TexVel=(0.02, -1.7) — direct GfxObj0x01004C42— hollow octagonal tube, TexVel=(0.02, -2.0) — direct GfxObj0x02000BA6,0x02000588,0x02000589— Setups, each containing a SHARED single 5cm dummy GfxObj (0x010001EC, 3 verts, 1 polygon) carrying a flash PES via DefaultScript
-
The PES referenced by weather Setups produces ONLY flash + sound effects. Recursive walk via
tools/PesChainAudit/confirmed: across all 5 weather PESes (0x33000453,0x33000428,0x3300042C,0x33000429,0x3300042D), there is exactly ONE CreateParticleHook anywhere in the chain, and it spawnsParticleEmitter 0x320002C2— a 10ms-lifetime, max-2-particle, zero-extent flash. The other 4 PESes are pure SoundTweaked + CallPES recursion loops (rolling thunder cadence). Zero rain particles. -
The rain streak texture is
0x050016A6— 256x256, R8G8B8 (no alpha), 99.4% black, sparse bright streaks. Engineered for additive blending. Not the texture itself broken — works as designed for an additive sparse-streak overlay. -
Retail uses
DEPTHTEST_ALWAYSandzfar*4for the weather pass, set at decomp0x00507063and0x00507055inGameSky::Draw(arg2=1). We triedDEPTHTEST_ALWAYSalone — it made the visual WORSE (rain mesh now covers everything as a wall over scene geometry). Whatever combination retail uses, it's not just depth-always.
Things we ruled out by trying:
- Camera-attached envelope spawn (made up r12-deepdive params; appeared as small dots near camera, not retail-like)
- Scaling the rain mesh down (turns it into an obvious cylinder around player)
- Unlit additive shader (no perceptible change)
- DEPTHTEST_ALWAYS alone (made it look worse)
- Setup loading for weather Setups (Setup parts are 5cm dummy quads, not visual)
Things we did NOT try and might be the answer:
- Retail's frame ordering — maybe the weather pass renders BEFORE the scene, not after. Worth checking the main render loop in retail (probably around
LScape::draworSmartBox::Draw). - Per-surface render state on the additive surface — maybe retail enables D3DRS_ALPHATESTENABLE + D3DRS_ALPHAREF for the rain Surface specifically, discarding pure-black texels.
- Texture sampling differences — mipmap settings, bilinear vs nearest, anisotropy. Retail's view of the cylinder wall at 113m goes through mipmap selection that might dramatically affect appearance.
- The DEPTHTEST_ALWAYS + zfar*4 combination together — we only tried depth alone. Maybe the zfar change is what makes it work.
- Multiple instances of the same mesh — retail might spawn the rain CPhysicsObj at SEVERAL anchor positions (not just the player), creating overlapping coverage. Worth re-checking
GameSky::CreateDeletePhysicsObjectsfor loop bounds. - The actual texture pixel content displayed in retail vs ours —
tools/TextureDump/already dumps the texture as PNG. Worth visually comparing what retail's rain looks like to verify the texture is correct.
Probe tools available (already on main, in tools/)
These are one-shot diagnostic console apps. Each builds standalone with Chorizite.DatReaderWriter NuGet + an env var pointing at the dat dir.
tools/PesChainAudit/— recursive PES walker. Given a starting0x33xxxxxxscript id, walks all CallPES references and dumps every hook + every referenced ParticleEmitter's params. Used to prove no weather PES has rain particles.tools/TextureDump/— dumps texture pixel stats (alpha histogram, brightness mean/stddev, max) and saves as PNG for visual inspection.tools/WeatherEnumerator/— enumerates all DayGroups in a Region, lists every weather SkyObject (Properties & 0x04), dumps GfxObj bounding boxes.tools/WeatherSetupProbe/— loads a Setup id, dumps every part's GfxObj, frame, scale, and surface info. Used to prove weather Setups are 5cm dummy quads.tools/SetupProbe/— already in main (similar to WeatherSetupProbe, slightly different focus).
Run from tools/<name>/ with dotnet run -c Release. Set ACDREAM_DAT_DIR if needed (defaults to %USERPROFILE%\Documents\Asheron's Call).
Other useful research artifacts on main
-
docs/research/2026-04-26-chorizite-pr-draft.md— upstream PR draft for the Chorizite/DatReaderWriter project. Five DBObj source files in theirGenerated/directory are stale duplicates that reference nonexistent enum values, causing them to be silently excluded from the published NuGet (ParticleEmitterInfo,Clothing,PaletteSet,DataIdMapper,DualDataIdMapper). The fix is to delete those files. You can file this PR independent of the rain investigation — it benefits the whole AC modding ecosystem. -
docs/research/2026-04-26-datreaderwriter-reference.md— DRW dev reference. Versioning, types we use, broken types catalog, upgrade procedure, NuGet vs vendored decision matrix. -
docs/research/named-retail/— the PDB-named retail decomp. Always grep here FIRST for any AC behavior question. Particularly relevant for sky/weather:GameSky::Drawat0x00506ff0GameSky::CreateDeletePhysicsObjectsat0x005073c0GameSky::MakeObjectat0x00506ee0GameSky::UpdatePositionat0x00506dd0LScape::weather_enabledat0x0081cbe9SmartBox::EnableWeatherat0x00451dd0
-
docs/research/deepdives/r12-weather-daynight.md— pre-decomp deep dive on weather mechanism. Several claims in r12 turned out to be incorrect (specifically the "rain is a camera-attached ParticleEmitter" claim was disproven by the 40-agent scan). Read it for context but don't trust the rain-mechanism details. -
docs/research/2026-04-23-lightning-real.md— corrected understanding of lightning (server-driven via opcode0xF754 PlayScript, not sky-walked). Relevant if you also work on Issue #2.
Suggested next investigation paths (priority-ranked)
-
Investigate Bug B first (the star bug) — much more concrete (UV mapping, texture wrap, geometry — all easily inspectable). May reveal the same kind of issue that affects rain. Specifically:
- Identify which SkyObject in DayGroup[0] (Sunny / clear night) renders the stars
- Dump that GfxObj's vertices + UVs via the existing
tools/SetupProbe/or extendWeatherEnumerator/ - Compare authored UV range to expected sky-dome coverage
- Check texture wrap mode in
SkyRenderer.cs(we set REPEAT for scrolling, CLAMP_TO_EDGE otherwise — stars probably need REPEAT or full UV remapping)
-
Re-grep retail decomp for the FULL render-state setup around the weather pass. The agents found DEPTHTEST_ALWAYS but maybe missed alpha test, blend mode, fog state, sample state, etc. Specifically dump the full
GameSky::Draw(arg2=1)body and verify EVERY D3D state call we should be replicating. -
Try DEPTHTEST_ALWAYS + zfar*4 TOGETHER. We only tried depth alone. The zfar change may matter for how the cylinder's far wall projects.
-
Investigate retail's frame ordering. Where in retail's main render loop does
GameSky::Draw(arg2=1)actually fire? Before or after scene meshes? If it's BEFORE, the depth test gives a totally different result than rendering after. -
File the Chorizite PR. Independent of rain. Easy contribution.
What "done" looks like
For Bug A (rain): standing in Holtburg during a Rainy DayGroup with ACDREAM_DAY_GROUP=7, rain visible IN the air between camera and character, not just on horizon. Looking up still matches retail. Walking forward, rain follows naturally.
For Bug B (stars): standing in Holtburg at night (ACDREAM_DAY_GROUP=<night-group-index>), stars visible as a full sky covering, NOT a small square in one corner.
Suggested first prompt for the next session
Read docs/research/2026-04-26-sky-investigation-handoff.md.
Tackle Bug B (stars rendering as a square in one corner) first
since it's more concrete. Use tools/WeatherEnumerator/ as a model
for a new probe that dumps the star SkyObject's geometry and UVs.
Iterate to a fix, verify visually, commit.
Once stars work, return to Bug A (rain) using the priority-ranked
investigation paths in §"Suggested next investigation paths" of
the handoff doc. The 40-agent findings have ruled out enough that
the next likely answer is in retail's full render-state setup
around the weather pass — specifically things the agents may have
missed beyond DEPTHTEST_ALWAYS.
Use a worktree per CLAUDE.md, and don't ask "should I continue"
between sub-steps.