# 2026-04-28 Sky Cloud Material Trace Context: Phase C.1 originally treated the Rainy/Cloudy sky visual as a SkyObject PES problem. Retail named-decomp and dat inspection disprove that for the broad cloud/ray layer. ## Retail Trace - `LScape::draw` (`0x00506330`) calls `GameSky::Draw(0)` before terrain and `GameSky::Draw(1)` after terrain. - `SkyDesc::GetSky` copies `pes_id`, but `GameSky::CreateDeletePhysicsObjects` compares/replaces only `gfx_id` and calls `GameSky::MakeObject(gfx_id, ...)`. The sky object PES id is not part of retail `GameSky` rendering. - `GameSky::UseTime` applies keyframe replace fields to instantiated sky objects: - `0x005076e1`: `CPhysicsObj::SetLuminosity(luminosity * 0.01)` - `0x00507715`: `CPhysicsObj::SetDiffusion(max_bright * 0.01)` - `0x00507747`: `CPhysicsObj::SetTranslucency(transparent * 0.01)` - `CMaterial::SetTranslucencySimple` (`0x005396f0`) writes material alpha as `1 - translucency`. - `CMaterial::SetDiffuseSimple` (`0x00539750`) writes material diffuse RGB. Therefore `SkyObjectReplace.MaxBright` is diffuse, not an emissive cap. - `D3DPolyRender::SetSurface` (`0x0059c4d0`) disables fixed-function fog alpha whenever the raw `SurfaceType.Additive` bit is set (`0x0059c882`), even when the earlier `Translucent + ClipMap` branch forces normal alpha blending. ## Dat Trace The broad Rainy/Cloudy layer is `GfxObj 0x01004C35`, not one of the tiny `0x020xxxxx` setup anchors: - `0x01004C35`: huge sky mesh, bbox roughly `20175 x 20175 x 1180`, UVs tile across the sheet. - Surface `0x08000023`: `Base1ClipMap | Translucent | Alpha | Additive` (`0x00010114`), `Translucency=0.25`, `Luminosity=0`, `Diffuse=1`. - Texture `0x060037AF`: 256x256 A8R8G8B8 cloud/ray texture. The setup ids observed in Rainy groups (`0x02000588`, `0x02000589`, `0x02000BA6`, `0x02000714`) are one-part dummy anchors with tiny `0x010001EC` geometry and default scripts/PES for sounds/flashes. They are not the broad cloud layer. ## Port Consequences - Keep per-SkyObject PES rendering debug-only until another retail path proves it is used. - Render `0x08000023` as final alpha blend because retail's translucent/clipmap branch overrides the raw additive blend. - Still disable sky fog for that surface because retail keys fog-alpha disable off the raw `Additive` bit. - Route `MaxBright` to diffuse (`uDiffuseFactor`) and `Luminosity` to emissive. - Use a final opacity multiplier for material/surface transparency before the fragment alpha write; dynamic keyframe transparency remains `1 - value`. ## WorldBuilder Cross-Check Cloned upstream `https://github.com/Chorizite/WorldBuilder.git` at commit `167788be6fce65f5ebe79eef07a0b7d28bd7aa81`. Its `Chorizite.OpenGLSDLBackend/Lib/SkyboxRenderManager.cs` renders sky objects camera-centered with depth off, but it is not a faithful retail oracle for sky tint: `GameScene.cs` has the skybox render call commented out, the manager always selects `DayGroups[0]`, and it uploads `SunlightColor = Vector3.Zero` / `AmbientColor = Vector3.One` for sky. `RegionInfo.cs` interpolates DayGroup[0] lighting for terrain/world objects, not the active retail DayGroup/weather sky. That explains why WorldBuilder cannot answer the missing green/purple Rainy sky tint directly. The actionable lesson is narrower: do not fog-paint the raw-additive cloud sheet itself. In acdream, non-additive sky layers now receive the keyframe fog tint so the broad background wash appears behind clouds, while surfaces with the raw Additive bit (notably `0x08000023`) keep fixed-function fog disabled and preserve the pink cloud/ray detail. WorldBuilder's regular object path does collect `Setup.DefaultScript` particle hooks (`ObjectMeshManager.CollectEmittersFromScript`) and instantiates them via `ObjectRenderManagerBase`, but its skybox manager does not use that setup/particle path for SkyObjects. Dat inspection also showed the canonical Rainy default script target `0x3300042C` is a sound-loop chain (`SoundTweaked` + `CallPES`), not the broad green tint or cloud ray layer. Additional renderer lessons from upstream WorldBuilder: - Particle blend is material-derived. `ParticleEmitterInfo` does not carry an additive flag; WorldBuilder reads `ObjectRenderData.Batches[0].IsAdditive` from the particle GfxObj surface. acdream now leaves DAT emitters non-additive by default and resolves particle blend from the selected particle surface. - Particles must be globally sorted back-to-front before drawing. Sorting only inside per-texture dictionaries can reorder translucent particles whenever multiple textures/blend states are active. - Particle quads come from the authored particle GfxObj bounds. Degenerate extents fall back to `1.0`, and point-sprite degrade mode applies a `0.9` base scale. - Texture decoding must try highres `RenderSurface` records after portal lookup and must zero alpha for black pixels on compressed clipmap textures. - WorldBuilder tracks UV wrap and cull mode per object batch. acdream's sky path already uses authored UV wrap, but shared object rendering still needs the same metadata carried through a later C.4 pass.