From e31113aaa2de449fb53e223de0dd2daeb31396cd Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Mon, 28 Sep 2026 13:32:53 -0400 Subject: [PATCH] Captured cave biomes in content - decorateNaturalCaves resolves coordinates of its own chunk through the captured column path; other coordinates keep the history dispatch --- .../generation/stage/IrisCarveModifier.java | 30 +++++++++++++++---- .../IrisCarveModifierCaveInputsTest.java | 26 ++++++++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/art/arcane/iris/generation/stage/IrisCarveModifier.java b/core/src/main/java/art/arcane/iris/generation/stage/IrisCarveModifier.java index d58ab98eb..7f58625b2 100644 --- a/core/src/main/java/art/arcane/iris/generation/stage/IrisCarveModifier.java +++ b/core/src/main/java/art/arcane/iris/generation/stage/IrisCarveModifier.java @@ -880,7 +880,7 @@ public class IrisCarveModifier extends EngineAssignedModifier chunk.release(); } } - CaveInputs resolver = new CaveInputs(getEngine()); + CaveInputs resolver = CaveInputs.forContentChunk(getEngine(), blockX >> 4, blockZ >> 4); Long2ObjectOpenHashMap caveBiomes = new Long2ObjectOpenHashMap<>(256); Map customBiomes = new HashMap<>(); int width = output.getWidth(); @@ -1163,19 +1163,39 @@ public class IrisCarveModifier extends EngineAssignedModifier private final ProceduralStream caveBiomes; private final ProceduralStream heights; private final CaveColumn[] columns; + private final boolean chunkLimited; + private final int chunkX; + private final int chunkZ; CaveInputs(Engine engine) { + this(engine, false, 0, 0); + } + + /** + * Inputs for the content stage of one chunk. A coordinate inside that chunk resolves under the chunk's + * own generation route, exactly the binding the natural terrain stage samples with, so it takes the + * captured column path; any other coordinate keeps the per-coordinate history dispatch. + */ + static CaveInputs forContentChunk(Engine engine, int chunkX, int chunkZ) { + return new CaveInputs(engine, true, chunkX, chunkZ); + } + + private CaveInputs(Engine engine, boolean contentChunk, int chunkX, int chunkZ) { this.engine = engine; complex = engine.getComplex(); dimension = engine.getDimension(); data = engine.getData(); height = engine.getHeight(); IrisContext context = IrisContext.get(); - boolean scoped = engine instanceof IrisEngine irisEngine + boolean generating = engine instanceof IrisEngine irisEngine && irisEngine.hasGenerationRuntimeScope() && !engine.getPlatformHooks().isMainThread() && context != null && context.getChunkContext() != null - && context.getChunkContext().getComplex() == complex - && context.getChunkContext().isNaturalTerrain(); + && context.getChunkContext().getComplex() == complex; + boolean natural = generating && context.getChunkContext().isNaturalTerrain(); + boolean scoped = natural || generating && contentChunk; + this.chunkLimited = !natural; + this.chunkX = chunkX; + this.chunkZ = chunkZ; snapshot = scoped ? IrisDimensionCarvingResolver.snapshot(engine) : null; minimumY = scoped ? engine.getWorld().minHeight() : 0; stack = scoped ? engine.getDimensionStackContext() : null; @@ -1186,7 +1206,7 @@ public class IrisCarveModifier extends EngineAssignedModifier } IrisBiome resolve(int x, int y, int z) { - if (snapshot == null) { + if (snapshot == null || chunkLimited && ((x >> 4) != chunkX || (z >> 4) != chunkZ)) { return engine.getCaveBiome(x, y, z, state); } IrisBiome configured = snapshot.resolveBiome(x, y + minimumY, z); diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierCaveInputsTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierCaveInputsTest.java index d07611c8e..5acfa3fb2 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierCaveInputsTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierCaveInputsTest.java @@ -26,6 +26,7 @@ import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.CALLS_REAL_METHODS; @@ -122,6 +123,31 @@ public class IrisCarveModifierCaveInputsTest { } } + @Test + public void contentChunkCapturesOnlyItsOwnColumnsAndDispatchesTheRestThroughHistory() { + try (Fixture fixture = new Fixture()) { + when(fixture.context.isNaturalTerrain()).thenReturn(false); + IrisBiome historical = biome("historical", 0); + when(fixture.engine.getCaveBiome(anyInt(), anyInt(), anyInt(), any(IrisDimensionCarvingResolver.State.class))).thenReturn(historical); + IrisCarveModifier.CaveInputs inputs = IrisCarveModifier.CaveInputs.forContentChunk(fixture.engine, -2, 1); + IrisDimensionCarvingResolver.State state = new IrisDimensionCarvingResolver.State(); + for (int x = -32; x <= -17; x++) { + for (int z = 16; z <= 31; z += 5) { + for (int y : new int[]{0, 30, 33, 34, 39, 40}) { + assertSame(fixture.scalar.getCaveBiome(x, y, z, state), inputs.resolve(x, y, z)); + } + } + } + int captured = fixture.samples.get(); + assertTrue(captured > 0); + assertSame(historical, inputs.resolve(-33, 20, 16)); + assertSame(historical, inputs.resolve(-16, 20, 16)); + assertSame(historical, inputs.resolve(-17, 20, 15)); + assertSame(historical, inputs.resolve(-17, 20, 32)); + assertEquals(captured, fixture.samples.get()); + } + } + private static IrisBiome biome(String key, int depth) { IrisBiome biome = new IrisBiome(); biome.setLoadKey(key);