Reject deposit clumps early

- Ellipsoid footprint dry run replays the build draws without building
- Terrain-scoped clumps above every column limit are dropped before building
- Surface biome filters no chunk column passes skip the deposit
This commit is contained in:
Brian Neumann-Fopiano committed 2026-09-28 11:35:56 -04:00
1 parent b47c7ae6aa
commit ec1984949b
4 files changed
+237 -13

No files matched your search

@@ -2,6 +2,8 @@ package art.arcane.iris.generation.decoration;
import art.arcane.iris.generation.biome.IrisBiome;
import art.arcane.iris.generation.block.IrisBlockData;
import art.arcane.iris.generation.block.VectorMap;
import art.arcane.iris.structure.object.IrisObject;
import art.arcane.iris.pack.loading.IrisData;
import art.arcane.volmlib.util.documentation.Description;
@@ -224,6 +226,38 @@ public class IrisDepositTuningTest {
assertNotEquals(17L * samples, largeBlocks);
}
@Test
public void ellipsoidFootprintConsumesTheBuildDrawsAndMatchesItsGeometry() {
IrisData data = mock(IrisData.class);
IrisDepositGenerator generator = generatorWithState(data, true);
generator.setShape(IrisDepositShape.VANILLA_ELLIPSOID);
for (int size : new int[]{0, 1, 3, 4, 9, 17, 25, 64}) {
generator.setMinSize(size).setMaxSize(size);
for (long seed = 0; seed < 300; seed++) {
RNG built = new RNG(seed);
IrisObject clump = generator.getClump(null, built, data);
RNG sampled = new RNG(seed);
IrisDepositGenerator.ClumpFootprint footprint = generator.sampleClumpFootprint(sampled, data);
assertNotNull(footprint);
assertEquals(clump.getW(), footprint.width());
assertEquals(clump.getBlocks().size() == 0, footprint.empty());
if (!footprint.empty()) {
int minimumY = Integer.MAX_VALUE;
VectorMap<NativeBlockState>.Cursor cursor = clump.getBlocks().cursor();
while (cursor.next()) {
minimumY = Math.min(minimumY, cursor.key().getBlockY());
}
assertEquals(minimumY, footprint.minY());
}
assertEquals(built.nextLong(), sampled.nextLong());
}
}
generator.setMinSize(65).setMaxSize(65);
assertNull(generator.sampleClumpFootprint(new RNG(1L), data));
generator.setShape(IrisDepositShape.VANILLA_SCATTERED).setMinSize(4).setMaxSize(4);
assertNull(generator.sampleClumpFootprint(new RNG(1L), data));
}
@Test
public void vanillaScatteredSizeIsAnUpperCandidateBound() {
IrisData data = mock(IrisData.class);
@@ -5,12 +5,14 @@ import art.arcane.iris.testsupport.KeyedBlockState;
import art.arcane.iris.pack.loading.IrisData;
import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.generation.biome.IrisBiome;
import art.arcane.iris.generation.decoration.IrisDepositBiomeScope;
import art.arcane.iris.generation.decoration.IrisDepositGenerator;
import art.arcane.iris.generation.decoration.IrisDepositHeightDistribution;
import art.arcane.iris.generation.decoration.IrisDepositShape;
import art.arcane.iris.generation.decoration.IrisDepositVariant;
import art.arcane.iris.generation.terrain.IrisDimension;
import art.arcane.iris.generation.terrain.IrisRegion;
import art.arcane.iris.structure.object.IrisObject;
import art.arcane.iris.spi.IrisPlatform;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
@@ -25,6 +27,7 @@ import art.arcane.volmlib.util.mantle.runtime.Mantle;
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
import art.arcane.volmlib.util.matter.Matter;
import art.arcane.volmlib.util.matter.MatterCavern;
import art.arcane.volmlib.util.math.RNG;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
@@ -99,13 +102,26 @@ public class IrisDepositModifierParityTest {
}
}
@Test
public void unmatchableSurfaceBiomeFiltersSkipClumpsWithoutChangingOutput() {
long[] expected = {-4609692219192155345L, 784L, 2118L, 53L, 67L, 11L};
AtomicInteger clumps = new AtomicInteger();
assertArrayEquals(expected, generateConfigured(101L, false, true, false, new AtomicInteger(), clumps));
assertEquals(0, clumps.get());
}
private long[] generateConfigured(long seed, boolean multicore) {
return generateConfigured(seed, multicore, true, false, new AtomicInteger());
}
@SuppressWarnings("unchecked")
private long[] generateConfigured(long seed, boolean multicore, boolean caveVariant, boolean enumerablePack,
AtomicInteger caveBiomeLookups) {
return generateConfigured(seed, multicore, caveVariant, enumerablePack, caveBiomeLookups, null);
}
@SuppressWarnings("unchecked")
private long[] generateConfigured(long seed, boolean multicore, boolean caveVariant, boolean enumerablePack,
AtomicInteger caveBiomeLookups, AtomicInteger unmatchableClumps) {
Engine engine = mock(Engine.class, RETURNS_DEEP_STUBS);
ChunkContext context = mock(ChunkContext.class, RETURNS_DEEP_STUBS);
IrisDimension dimension = new IrisDimension();
@@ -136,6 +152,16 @@ public class IrisDepositModifierParityTest {
gold.setExcludedBiomes(new KList<>("allowed"));
gold.setDiscardChanceOnAirExposure(0.3D);
surface.getDeposits().add(gold);
if (unmatchableClumps != null) {
IrisDepositGenerator unmatchable = new CountingPaletteGenerator(state("emerald_ore"), unmatchableClumps)
.setShape(IrisDepositShape.VANILLA_ELLIPSOID).setMinSize(9).setMaxSize(9)
.setMinPerChunk(90).setMaxPerChunk(90).setMinHeight(-4).setMaxHeight(44)
.setHeightDistribution(IrisDepositHeightDistribution.UNIFORM).setSurfaceClearance(0)
.setReplaceableBlocks(new KList<>("stone"))
.setBiomeScope(IrisDepositBiomeScope.SURFACE)
.setIncludedBiomes(new KList<>("elsewhere"));
surface.getDeposits().add(unmatchable);
}
when(engine.getHeight()).thenReturn(48);
when(engine.getMinHeight()).thenReturn(-64);
when(engine.getSeedManager().getDeposit()).thenReturn(seed);
@@ -404,7 +430,7 @@ public class IrisDepositModifierParityTest {
}
}
private static final class PaletteGenerator extends IrisDepositGenerator {
private static class PaletteGenerator extends IrisDepositGenerator {
private final KList<NativeBlockState> blocks;
private PaletteGenerator(NativeBlockState block) {
@@ -416,4 +442,25 @@ public class IrisDepositModifierParityTest {
return blocks;
}
}
private static final class CountingPaletteGenerator extends PaletteGenerator {
private final AtomicInteger clumps;
private CountingPaletteGenerator(NativeBlockState block, AtomicInteger clumps) {
super(block);
this.clumps = clumps;
}
@Override
public IrisObject getClump(Engine engine, RNG rng, IrisData data) {
clumps.incrementAndGet();
return super.getClump(engine, rng, data);
}
@Override
public ClumpFootprint sampleClumpFootprint(RNG rng, IrisData data) {
clumps.incrementAndGet();
return super.sampleClumpFootprint(rng, data);
}
}
}