6a8390f11f28e6ec292fa9d21630054a591232bb
⚠️ THIS IS THE ONE COMMIT OF THIS RUN WHOSE FAILURE MODE IS A HOLE, NOT A REGRESSION. A non-Mixed verdict makes the world skip GenerateMesh entirely: no triangles, no collision, invisible until a player falls through. Read VoxelForge.OpStack.ClassifyTileSoundness before trusting it. WHAT CHANGED ClassifyTile used to `return Mixed` without a call for every cave archetype ("archétype cave [...] pas prouvable en v1"). It now builds that strate's stack and folds ClassifyBox. SurfaceWorld and bedrock gaps keep their hand-written proofs untouched -- the exact-lattice column test is strictly better than any box bound, so the stack has nothing to add there. ONE DEFINITION OF THE STACK MAPPING, and that is the load-bearing part GetDensityAt's ~90-line build switch is extracted into VF_BuildOpStackForChunk and both callers now use it. A second copy would be the worst bug available here: a tile skipped on the verdict of a stack that is not the one producing its density is precisely a hole. A "keep these in sync" comment would not have been enough; there had to be only one. Params are passed in by pointer, never fetched inside, because both callers already have them. SIX GUARDS, ALL FAILING TO MIXED, none giving the benefit of the doubt 1. The strate must actually opt in -- on EVERY chunk coord the box touches, not just the one that triggered the attempt. Otherwise the classifier judges a field the mesher will not produce. 2. One cave slot per tile. Two slots means two param sets, and a stack answers only for its strate. 3. No mixed cave/surface/gap tile: the cave stack's box would cover Z belonging to another strate. 4. The params must be BIT-IDENTICAL across every chunk coord the box touches. This is the guard that matters, and it exists because of the finding committed earlier today: GetGenerationParams blends params inside a strate (Alpha depends on chunk Z for Gradient, and on chunk XY as well for Interleaved), so one stack genuinely cannot represent a tile that straddles a transition band. Memcmp on POD: differing padding can only produce a false MISMATCH, i.e. one Mixed too many. 5. A 27-chunk-coord cap, so a very wide tile does not pay for the check. We give up the gain, never the safety. 6. The disturbances are folded by hand (chasm ⇒ CarveOnly, bridge/ridge ⇒ FillOnly), because DECOMPOSITION 10.2 leaves them OUTSIDE the stack -- GetDensityAt applies them after the negate. A verdict that ignored them would be wrong exactly where they act. Same inequalities the SurfaceWorld branch already uses. The diff layer needs no new guard: ClassifyTile already returns Mixed for any tile with player mods in range, before any of this. TEST: VoxelForge.OpStack.ClassifyTileSoundness -- the same brute-force oracle as the existing ClassifyTileSoundness, on a world where every strate opted in. It does not check the fold (that is BoxVerdictFold) or the operators (those are the eight equivalence tests); it checks the WIRING. The number to read first is the count of tiles actually brute-forced: zero non-Mixed verdicts would mean the test proved nothing, so that case is an ERROR rather than a quiet pass. Its failure message lists the four suspects in the order worth checking. FIXTURE: FTestWorld::Build gains a bUseOperatorStack parameter (default false, so the thirteen existing tests still exercise the switch), and every test world now gets a PROCESS-UNIQUE LayoutVersion. That second change fixes a real cross-test hazard that was only ever hidden by an accident: PassagesVersion is per-instance and starts at 0, so two FTestWorlds both reported 1, and GetDensityAt's per-chunk caches are keyed on (ChunkCoord, LayoutVersion) -- one world could be served the previous world's params AND its CP_UseOpStack flag. Invisible while every world agreed the flag was false. The first world that ticks it removes that coincidence, in both directions. WHAT THIS BUYS TODAY: Maze, FlatPlain/CrystalChamber, VerticalShafts and FloatingIslands can now prove tiles in production, which is where the measured skipping (Maze 23/60, slabs 36-40/60) turns into frames. TunnelNetwork and Underwater still prove nothing: their chain dies at FRoomGraphSource, which answers Both with unknown amplitude. Making it answer spatially means building the SDF cache for the queried box -- now worth doing, since a skipped tile saves 30k+ density evaluations, and the amplitude fold plus the modifiers' Identity inheritance are already in place to receive it. That is the next piece. And nothing here changes Jahni's current world: no strate asset has bUseOperatorStack ticked, so every guard above is unreachable in his project until he ticks one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Description
No description provided
Languages
C++
97.6%
C
2.3%
C#
0.1%