6ec60099d6
STAGE B group 3 of 5. The stack is now 15 ops: ConstantRock -> RoomGraph -> SdfCarve -> Roughness -> Terrace -> LayerLines -> Ribbing -> Overhang -> Cliff -> Scallop -> Arch -> Worms -> [structural x3] Still NOT wired: UsesOperatorStackForChunk returns false for TunnelNetwork. TRANSCRIBED - FCaveOverhangMod: fBM with Z frequency at 0.15x of XY, positive lobe only (rock extends INTO the cave, never away), quadratic fade. Note this is the THIRD unrelated thing called "overhang" in the plugin -- FOverhangShelfMod (SurfaceWorld) borrows the uphill terrain height and shares nothing with it but the name. - FScallopMod: cellular noise, positive lobe carved out. Second consumer of VoxelNoise::Cellular3D, which is the second reason B1 shared that body instead of copying it. - FCaveArchMod: the first ROOM-RELATIVE modifier -- it needs the room's hash, centre and radii, so it is what makes FRoomGraphSource::GetNearestRoomIdx() necessary. Its gate is NOT the shared one: CaveSDF < SDFBlendRadius (inside the cave), not < SDFBlendRadius*3. PORTED AS-IS THOUGH THE ORIGINAL'S OWN COMMENT DISAGREES WITH ITS CODE FCaveCliffMod's comment promises "sample density at Z+1 and Z-1, compute vertical gradient". The code samples nothing: it draws a Perlin whose Z frequency is 3x its XY frequency and names the result VertGrad. It is a gradient-shaped PROXY, decorrelated from the wall's actual slope; the multiply by CaveSDF still gives it the right SIGN either side of the surface, which is why it produces steeper faces at all. Fixing it would change the world, and saying nothing would leave it to be "fixed" later by someone reading the comment instead of the code. Ported, documented in the op, recorded in OPSTACK-PROGRESS. ONE DELIBERATE ADDITION FCaveArchMod bounds-checks NearestRoomIdx with IsValidIndex before indexing; the original tests only >= 0. The index comes from EvaluateSDFCached so it is valid by construction, which is exactly why this can never change output -- only prevent a crash if that invariant ever breaks. Same class of decision as the params fingerprint in the cache key: err on cost, never on result. Overhang is FillOnly, Scallop is CarveOnly, Arch is FillOnly; only Cliff needs Both. Four of the eight modifiers ported so far keep a usable direction for the fold, which matters for the DECOMPOSITION 0.2 work later. TEST (same commit): op count 11 -> 15, four params groups move into EnableTunnelFeatures, four more per-operator coverage probes. ArchDensity is set to 0.9 rather than its 0.1 default and the reason is written down: at 0.1 a room draws ~0.3 arches and the probe would report zero with nothing wrong. Test coverage settings are not production values. IF THIS GROUP IS WRONG: diffs concentrate inside open cave (arch) or in a band |SDF| < range (the other three), and the roughness/terrace probes stay green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>