Merged terrain assembly improvements

- Ceiling layer thickness resolves independently from surface layers
- Locked strata repeat correctly above the internal height reference
- Cave scratch releases generation references after each pass
- Terrain actuators omit redundant setup and checks
This commit is contained in:
Brian Neumann-Fopiano committed 2026-09-06 03:16:51 -04:00
commit a68cad5893
16 files changed
+446 -223

No files matched your search

@@ -22,24 +22,15 @@ import art.arcane.volmlib.util.json.JSONArray;
import art.arcane.volmlib.util.json.JSONObject;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
* Validates biome layer stacks. caveCeilingLayers reuses the height generators built from layers,
* so a biome with more ceiling entries than surface entries has no generator for the extras; the
* engine skips them, and this validator surfaces the mistake to the author at validate time.
*/
final class PackBiomeLayerValidator {
private static final int DEFAULT_LAYER_COUNT = 1;
private PackBiomeLayerValidator() {
}
static List<String> validateCeilingLayerCounts(File biomesFolder) {
static List<String> validateLayers(File biomesFolder) {
List<String> blockingErrors = new ArrayList<>();
if (biomesFolder == null || !biomesFolder.isDirectory()) {
return blockingErrors;
@@ -49,24 +40,13 @@ final class PackBiomeLayerValidator {
biomeFiles.sort(Comparator.comparing(File::getPath));
for (File biomeFile : biomeFiles) {
String biomeKey = PackValidationIo.deriveKey(biomesFolder, biomeFile);
JSONObject biome;
try {
biome = new JSONObject(Files.readString(biomeFile.toPath(), StandardCharsets.UTF_8));
} catch (Throwable e) {
// Invalid JSON is reported by the graph validators; layer counts have nothing to add.
JSONObject biome = PackValidationIo.readJson(biomeFile);
if (biome == null) {
continue;
}
Integer layers = arrayLength(biome, "layers", biomeKey, DEFAULT_LAYER_COUNT, blockingErrors);
Integer ceiling = arrayLength(biome, "caveCeilingLayers", biomeKey, 0, blockingErrors);
if (layers == null || ceiling == null) {
continue;
}
if (ceiling > layers) {
blockingErrors.add("Biome '" + biomeKey + "' declares " + ceiling + " caveCeilingLayers but only "
+ layers + " layers. caveCeilingLayers reuses the layers height generators and must not have more entries.");
}
validateLayerArray(biome, "layers", biomeKey, blockingErrors);
validateLayerArray(biome, "caveCeilingLayers", biomeKey, blockingErrors);
}
return blockingErrors;
}
@@ -126,17 +106,15 @@ final class PackBiomeLayerValidator {
}
}
private static Integer arrayLength(JSONObject biome, String field, String biomeKey, int defaultCount,
private static void validateLayerArray(JSONObject biome, String field, String biomeKey,
List<String> blockingErrors) {
if (!biome.has(field) || biome.isNull(field)) {
return defaultCount;
return;
}
JSONArray array = biome.optJSONArray(field);
if (array == null) {
blockingErrors.add("Biome '" + biomeKey + "' " + field + " must be an array.");
return null;
}
return array.length();
}
}
@@ -150,7 +150,7 @@ public final class PackValidator {
new File(packFolder, "spawners"), new File(packFolder, "entities")));
blockingErrors.addAll(PackSpawnValidator.validateCustomBiomeSpawns(
new File(packFolder, "biomes"), PackSpawnValidator::resolveEntitySpawnCategory));
blockingErrors.addAll(PackBiomeLayerValidator.validateCeilingLayerCounts(new File(packFolder, "biomes")));
blockingErrors.addAll(PackBiomeLayerValidator.validateLayers(new File(packFolder, "biomes")));
blockingErrors.addAll(PackBiomeLayerValidator.validateDecoratorPalettes(
new File(packFolder, "biomes"), new File(packFolder, "snippet/decorator")));
PackStyledRangeDefaultValidator.Validation styledRanges = PackStyledRangeDefaultValidator.validate(packFolder);
@@ -41,7 +41,6 @@ import art.arcane.volmlib.util.math.RNG;
import art.arcane.volmlib.util.matter.Matter;
import art.arcane.volmlib.util.matter.MatterBiomeInject;
import art.arcane.volmlib.util.matter.slices.BiomeInjectMatter;
import art.arcane.volmlib.util.scheduling.ChronoLatch;
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
import java.util.List;
@@ -49,7 +48,6 @@ import java.util.Objects;
public class IrisBiomeActuator extends EngineAssignedActuator<PlatformBiome> {
private final RNG rng;
private final ChronoLatch cl = new ChronoLatch(5000);
private final KMap<String, ResolvedBiome> resolvedBiomes = new KMap<>();
private final KMap<String, ResolvedBiome> resolvedPhysicalBiomes = new KMap<>();
@@ -46,11 +46,7 @@ import art.arcane.iris.util.common.data.B;
import lombok.Getter;
public class IrisTerrainNormalActuator extends EngineAssignedActuator<PlatformBlockState> {
private static final PlatformBlockState AIR = B.getState("AIR");
private static final PlatformBlockState BEDROCK = B.getState("BEDROCK");
private static final PlatformBlockState LAVA = B.getState("LAVA");
private static final PlatformBlockState GLASS = B.getState("GLASS");
private static final PlatformBlockState CAVE_AIR = B.getState("CAVE_AIR");
@Getter
private final RNG rng;
@Getter
@@ -83,11 +79,6 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<PlatformBl
*/
@BlockCoordinates
public void terrainSliver(int x, int z, int xf, Hunk<PlatformBlockState> h, ChunkContext context) {
terrainSliverOptimized(x, z, xf, h, context);
}
@BlockCoordinates
private void terrainSliverOptimized(int x, int z, int xf, Hunk<PlatformBlockState> h, ChunkContext context) {
int chunkHeight = h.getHeight();
int chunkDepth = h.getDepth();
IrisDimension dimension = getDimension();
@@ -42,9 +42,7 @@ public final class IrisTransitionGeometryActuator extends EngineAssignedComponen
return;
}
GeometrySource source = new GeometrySource(blocks, biomes, getEngine().getMinHeight(), context);
if (plan != null && plan.hasTransitionAtChunk(x >> 4, z >> 4)) {
blendGeometry(x, z, blocks, context, source, plan);
}
blendGeometry(x, z, blocks, context, source, plan);
}
public static SavedTerrainChunk capture(int x, int z, Hunk<PlatformBlockState> blocks,
@@ -22,6 +22,8 @@ import art.arcane.volmlib.util.matter.Matter;
import art.arcane.volmlib.util.matter.MatterCavern;
import art.arcane.volmlib.util.matter.MatterSlice;
import java.util.Arrays;
final class CaveCarveScratch {
final int[] columnMaxY = new int[256];
final int[] fluidMaxY = new int[256];
@@ -70,4 +72,9 @@ final class CaveCarveScratch {
int adaptiveGeometryStep = -1;
int adaptiveGeometryAxisCells = -1;
boolean fullWeightsInitialized;
void releaseSections() {
Arrays.fill(sectionMatter, null);
Arrays.fill(sectionSlices, null);
}
}
@@ -436,6 +436,7 @@ public class IrisCaveCarver3D {
return carved;
} finally {
scratchCache.get().releaseSections();
carving.set(nested);
if (nested) {
scratchCache.set(previousScratch);
@@ -2695,10 +2696,6 @@ public class IrisCaveCarver3D {
return weight;
}
private double signed(double value) {
return (value * 2D) - 1D;
}
private double applySurfaceCeilingFade(
CaveCarveScratch scratch,
double threshold,
@@ -19,6 +19,7 @@
package art.arcane.iris.engine.mantle.components;
import art.arcane.iris.engine.IrisComplex;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.UpperDimensionContext;
import art.arcane.iris.engine.mantle.MatterGenerationPhase;
import art.arcane.iris.engine.mantle.ComponentFlag;
@@ -36,7 +37,6 @@ import art.arcane.iris.util.common.data.B;
import art.arcane.iris.util.project.context.ChunkContext;
import art.arcane.iris.util.project.stream.ProceduralStream;
import art.arcane.iris.util.project.stream.utility.ChunkFillableDoubleStream2D;
import art.arcane.iris.util.simd.SimdKernels;
import art.arcane.iris.util.simd.SimdSupport;
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
import art.arcane.volmlib.util.mantle.flag.ReservedFlag;
@@ -58,6 +58,7 @@ public class MantleCarvingComponent extends IrisMantleComponent {
private static final double MIN_WEIGHT = 0.08D;
private static final double THRESHOLD_PENALTY = 0.24D;
private static final int MAX_BLENDED_PROFILE_PASSES = 2;
private static final int MAX_POOLED_WEIGHT_BUFFERS = 64;
private static final int KERNEL_WIDTH = (BLEND_RADIUS * 2) + 1;
private static final int KERNEL_SIZE = KERNEL_WIDTH * KERNEL_WIDTH;
private static final int[] KERNEL_DX = new int[KERNEL_SIZE];
@@ -107,8 +108,25 @@ public class MantleCarvingComponent extends IrisMantleComponent {
if (!complex.allowsMantleChunkWrite(x, z)) {
return;
}
BlendScratch previous = BLEND_SCRATCH.get();
BlendScratch scratch = previous.inUse ? new BlendScratch() : previous;
if (scratch != previous) {
BLEND_SCRATCH.set(scratch);
}
scratch.inUse = true;
try {
generateLayer(writer, x, z, context, scratch);
} finally {
scratch.release();
if (scratch != previous) {
BLEND_SCRATCH.set(previous);
}
}
}
private void generateLayer(MantleWriter writer, int x, int z, ChunkContext context, BlendScratch blendScratch) {
IrisComplex complex = context.getComplex();
IrisDimensionCarvingResolver.State resolverState = new IrisDimensionCarvingResolver.State();
BlendScratch blendScratch = BLEND_SCRATCH.get();
int[] chunkSurfaceHeights = prepareChunkSurfaceHeights(x, z, context, blendScratch.chunkSurfaceHeights);
PrecisionStopwatch resolveStopwatch = PrecisionStopwatch.start();
List<WeightedProfile> weightedProfiles = resolveWeightedProfiles(x, z, complex, resolverState);
@@ -217,12 +235,12 @@ public class MantleCarvingComponent extends IrisMantleComponent {
BlendScratch blendScratch = BLEND_SCRATCH.get();
IrisCaveProfile[] profileField = blendScratch.profileField;
Map<IrisCaveProfile, double[]> columnProfileWeights = blendScratch.columnProfileWeights;
IdentityHashMap<IrisCaveProfile, Boolean> activeProfiles = blendScratch.activeProfiles;
List<IrisCaveProfile> profileOrder = blendScratch.profileOrder;
IrisCaveProfile[] kernelProfiles = blendScratch.kernelProfiles;
double[] kernelProfileWeights = blendScratch.kernelProfileWeights;
activeProfiles.clear();
columnProfileWeights.clear();
profileOrder.clear();
blendScratch.nextWeightBuffer = 0;
fillProfileField(profileField, chunkX, chunkZ, complex, resolverState, blendScratch);
for (int localX = 0; localX < CHUNK_SIZE; localX++) {
@@ -270,12 +288,8 @@ public class MantleCarvingComponent extends IrisMantleComponent {
double[] weights = columnProfileWeights.get(profile);
if (weights == null) {
weights = new double[CHUNK_AREA];
weights = blendScratch.acquireWeights();
columnProfileWeights.put(profile, weights);
} else if (!activeProfiles.containsKey(profile)) {
Arrays.fill(weights, 0D);
}
if (activeProfiles.put(profile, Boolean.TRUE) == null) {
profileOrder.add(profile);
}
weights[columnIndex] = columnWeight;
@@ -316,28 +330,27 @@ public class MantleCarvingComponent extends IrisMantleComponent {
return weightedProfiles;
}
Map<IrisDimensionCarvingEntry, IrisDimensionCarvingEntry[]> dimensionColumnPlans = blendScratch.dimensionColumnPlans;
dimensionColumnPlans.clear();
Engine engine = getEngineMantle().getEngine();
int baseX = PowerOfTwoCoordinates.chunkToBlock(chunkX);
int baseZ = PowerOfTwoCoordinates.chunkToBlock(chunkZ);
for (IrisDimensionCarvingEntry entry : entries) {
if (entry == null || !entry.isEnabled()) {
continue;
}
IrisBiome rootBiome = IrisDimensionCarvingResolver.resolveEntryBiome(getEngineMantle().getEngine(), entry, resolverState);
IrisBiome rootBiome = IrisDimensionCarvingResolver.resolveEntryBiome(engine, entry, resolverState);
if (rootBiome == null) {
continue;
}
IrisDimensionCarvingEntry[] columnPlan = dimensionColumnPlans.computeIfAbsent(entry, key -> new IrisDimensionCarvingEntry[CHUNK_AREA]);
buildDimensionColumnPlan(columnPlan, chunkX, chunkZ, entry, resolverState);
Map<IrisCaveProfile, double[]> rootProfileColumnWeights = new IdentityHashMap<>();
List<IrisCaveProfile> rootProfileOrder = new ArrayList<>();
IrisRange worldYRange = entry.getWorldYRange();
for (int columnIndex = 0; columnIndex < CHUNK_AREA; columnIndex++) {
IrisDimensionCarvingEntry resolvedEntry = columnPlan[columnIndex];
IrisBiome resolvedBiome = IrisDimensionCarvingResolver.resolveEntryBiome(getEngineMantle().getEngine(), resolvedEntry, resolverState);
IrisDimensionCarvingEntry resolvedEntry = IrisDimensionCarvingResolver.resolveFromRoot(
engine, entry, baseX + (columnIndex >> 4), baseZ + (columnIndex & 15), resolverState);
IrisBiome resolvedBiome = IrisDimensionCarvingResolver.resolveEntryBiome(engine, resolvedEntry, resolverState);
if (resolvedBiome == null) {
continue;
}
@@ -349,7 +362,7 @@ public class MantleCarvingComponent extends IrisMantleComponent {
double[] columnWeights = rootProfileColumnWeights.get(profile);
if (columnWeights == null) {
columnWeights = new double[CHUNK_AREA];
columnWeights = blendScratch.acquireWeights();
rootProfileColumnWeights.put(profile, columnWeights);
rootProfileOrder.add(profile);
}
@@ -364,19 +377,6 @@ public class MantleCarvingComponent extends IrisMantleComponent {
return weightedProfiles;
}
private void buildDimensionColumnPlan(IrisDimensionCarvingEntry[] columnPlan, int chunkX, int chunkZ, IrisDimensionCarvingEntry entry, IrisDimensionCarvingResolver.State resolverState) {
int baseX = PowerOfTwoCoordinates.chunkToBlock(chunkX);
int baseZ = PowerOfTwoCoordinates.chunkToBlock(chunkZ);
for (int localX = 0; localX < CHUNK_SIZE; localX++) {
int worldX = baseX + localX;
for (int localZ = 0; localZ < CHUNK_SIZE; localZ++) {
int worldZ = baseZ + localZ;
int columnIndex = PowerOfTwoCoordinates.packLocal16(localX, localZ);
columnPlan[columnIndex] = IrisDimensionCarvingResolver.resolveFromRoot(getEngineMantle().getEngine(), entry, worldX, worldZ, resolverState);
}
}
}
private void fillProfileField(IrisCaveProfile[] profileField, int chunkX, int chunkZ, IrisComplex complex, IrisDimensionCarvingResolver.State resolverState, BlendScratch blendScratch) {
int startX = PowerOfTwoCoordinates.chunkToBlock(chunkX) - BLEND_RADIUS;
int startZ = PowerOfTwoCoordinates.chunkToBlock(chunkZ) - BLEND_RADIUS;
@@ -698,8 +698,7 @@ public class MantleCarvingComponent extends IrisMantleComponent {
private final IrisCaveProfile[] kernelProfiles = new IrisCaveProfile[KERNEL_SIZE];
private final double[] kernelProfileWeights = new double[KERNEL_SIZE];
private final IdentityHashMap<IrisCaveProfile, double[]> columnProfileWeights = new IdentityHashMap<>();
private final IdentityHashMap<IrisDimensionCarvingEntry, IrisDimensionCarvingEntry[]> dimensionColumnPlans = new IdentityHashMap<>();
private final IdentityHashMap<IrisCaveProfile, Boolean> activeProfiles = new IdentityHashMap<>();
private final List<double[]> weightBuffers = new ArrayList<>();
private final List<IrisCaveProfile> profileOrder = new ArrayList<>();
private final double[] fieldSurfaceHeights = new double[FIELD_SIZE * FIELD_SIZE];
private final double[] fieldFluidHeights = new double[FIELD_SIZE * FIELD_SIZE];
@@ -710,5 +709,33 @@ public class MantleCarvingComponent extends IrisMantleComponent {
private final IrisBiome[] fieldCaveBiomes = new IrisBiome[FIELD_SIZE * FIELD_SIZE];
private final int[] chunkSurfaceHeights = new int[CHUNK_AREA];
private final double[] chunkSurfaceHeightSamples = new double[CHUNK_AREA];
private int nextWeightBuffer;
private boolean inUse;
private double[] acquireWeights() {
if (nextWeightBuffer < weightBuffers.size()) {
double[] weights = weightBuffers.get(nextWeightBuffer++);
Arrays.fill(weights, 0D);
return weights;
}
double[] weights = new double[CHUNK_AREA];
if (weightBuffers.size() < MAX_POOLED_WEIGHT_BUFFERS) {
weightBuffers.add(weights);
nextWeightBuffer++;
}
return weights;
}
private void release() {
Arrays.fill(profileField, null);
Arrays.fill(kernelProfiles, null);
Arrays.fill(fieldRegions, null);
Arrays.fill(fieldSurfaceBiomes, null);
Arrays.fill(fieldCaveBiomes, null);
columnProfileWeights.clear();
profileOrder.clear();
nextWeightBuffer = 0;
inUse = false;
}
}
}
@@ -92,6 +92,7 @@ public class IrisBiome extends IrisRegistrant implements IRare {
private final transient AtomicCache<IrisBiome> realCarveBiome = new AtomicCache<>();
private final transient AtomicCache<KList<IrisBiome>> realChildren = new AtomicCache<>();
private final transient AtomicCache<KList<CNG>> layerHeightGenerators = new AtomicCache<>();
private final transient AtomicCache<KList<CNG>> layerCeilingHeightGenerators = new AtomicCache<>();
private final transient AtomicCache<KList<CNG>> layerSeaHeightGenerators = new AtomicCache<>();
private final transient AtomicCache<KList<IrisOreGenerator>> surfaceOreCache = new AtomicCache<>();
private final transient AtomicCache<KList<IrisOreGenerator>> undergroundOreCache = new AtomicCache<>();
@@ -74,7 +74,7 @@ final class IrisBiomeLayerGenerator {
IrisBiomePaletteLayer layer = layers.get(i);
double zoom = layer.getZoom();
CNG hgen = heightGenerators.get(i);
double d = hgen.fit(layer.getMinHeight(), layer.getMaxHeight(), wx / zoom, wz / zoom);
int d = hgen.fit(layer.getMinHeight(), layer.getMaxHeight(), wx / zoom, wz / zoom);
IrisSlopeClip sc = layer.getSlopeCondition();
@@ -88,17 +88,7 @@ final class IrisBiomeLayerGenerator {
continue;
}
for (int j = 0; j < d; j++) {
if (data.size() >= maxDepth) {
break;
}
try {
data.add(layer.get(random, i + j, (wx + j) / zoom, j, (wz - j) / zoom, rdata));
} catch (Throwable e) {
IrisLogging.reportError(e);
}
}
appendLayer(data, layer, i, d, wx, wz, random, maxDepth, rdata);
if (data.size() >= maxDepth) {
break;
@@ -132,31 +122,20 @@ final class IrisBiomeLayerGenerator {
return data;
}
KList<CNG> heightGenerators = getLayerHeightGenerators(biome, random, rdata);
// Ceiling layers reuse the surface-layer height generators, so entries beyond layers.size() have no generator; skip them.
int usableLayers = Math.min(layerCount, heightGenerators.size());
KList<CNG> heightGenerators = getHeightGenerators(
biome.getLayerCeilingHeightGenerators(), ceilingLayers, 7235, random, rdata);
for (int i = 0; i < usableLayers; i++) {
for (int i = 0; i < layerCount; i++) {
IrisBiomePaletteLayer layer = ceilingLayers.get(i);
double zoom = layer.getZoom();
CNG hgen = heightGenerators.get(i);
double d = hgen.fit(layer.getMinHeight(), layer.getMaxHeight(), wx / zoom, wz / zoom);
int d = hgen.fit(layer.getMinHeight(), layer.getMaxHeight(), wx / zoom, wz / zoom);
if (d <= 0) {
continue;
}
for (int j = 0; j < d; j++) {
if (data.size() >= maxDepth) {
break;
}
try {
data.add(layer.get(random, i + j, (wx + j) / zoom, j, (wz - j) / zoom, rdata));
} catch (Throwable e) {
IrisLogging.reportError(e);
}
}
appendLayer(data, layer, i, d, wx, wz, random, maxDepth, rdata);
if (data.size() >= maxDepth) {
break;
@@ -198,7 +177,7 @@ final class IrisBiomeLayerGenerator {
IrisBiomePaletteLayer layer = layers.get(i);
double zoom = layer.getZoom();
CNG hgen = heightGenerators.get(i);
double d = hgen.fit(layer.getMinHeight(), layer.getMaxHeight(), wx / zoom, wz / zoom);
int d = hgen.fit(layer.getMinHeight(), layer.getMaxHeight(), wx / zoom, wz / zoom);
IrisSlopeClip sc = layer.getSlopeCondition();
@@ -212,13 +191,7 @@ final class IrisBiomeLayerGenerator {
continue;
}
for (int j = 0; j < d; j++) {
try {
data.add(layer.get(random, i + j, (wx + j) / zoom, j, (wz - j) / zoom, rdata));
} catch (Throwable e) {
IrisLogging.reportError(e);
}
}
appendLayer(data, layer, i, d, wx, wz, random, Integer.MAX_VALUE, rdata);
}
}
@@ -227,9 +200,8 @@ final class IrisBiomeLayerGenerator {
}
for (int i = 0; i < maxDepth; i++) {
int offset = (512 - height) - i;
int index = offset % data.size();
real.add(data.get(Math.max(index, 0)));
long offset = 512L - height - i;
real.add(data.get(Math.floorMod(offset, data.size())));
}
return real;
@@ -264,17 +236,7 @@ final class IrisBiomeLayerGenerator {
continue;
}
for (int j = 0; j < d; j++) {
if (data.size() >= maxDepth) {
break;
}
try {
data.add(layer.get(random, i + j, (wx + j) / zoom, j, (wz - j) / zoom, rdata));
} catch (Throwable e) {
IrisLogging.reportError(e);
}
}
appendLayer(data, layer, i, d, wx, wz, random, maxDepth, rdata);
if (data.size() >= maxDepth) {
break;
@@ -285,7 +247,16 @@ final class IrisBiomeLayerGenerator {
}
static KList<CNG> getLayerHeightGenerators(IrisBiome biome, RNG rng, IrisData rdata) {
AtomicCache<KList<CNG>> cache = biome.getLayerHeightGenerators();
return getHeightGenerators(biome.getLayerHeightGenerators(), biome.getLayers(), 7235, rng, rdata);
}
static KList<CNG> getLayerSeaHeightGenerators(IrisBiome biome, RNG rng, IrisData data) {
return getHeightGenerators(biome.getLayerSeaHeightGenerators(), biome.getSeaLayers(), 7735, rng, data);
}
private static KList<CNG> getHeightGenerators(AtomicCache<KList<CNG>> cache,
KList<IrisBiomePaletteLayer> layers,
int seedOffset, RNG rng, IrisData data) {
KList<CNG> cached = cache.getIfPresent();
if (cached != null) {
@@ -296,36 +267,28 @@ final class IrisBiomeLayerGenerator {
{
KList<CNG> layerHeightGenerators = new KList<>();
int m = 7235;
int m = seedOffset;
for (IrisBiomePaletteLayer i : biome.getLayers()) {
layerHeightGenerators.add(i.getHeightGenerator(rng.nextParallelRNG((m++) * m * m * m), rdata));
for (IrisBiomePaletteLayer layer : layers) {
layerHeightGenerators.add(layer.getHeightGenerator(rng.nextParallelRNG((m++) * m * m * m), data));
}
return layerHeightGenerators;
});
}
static KList<CNG> getLayerSeaHeightGenerators(IrisBiome biome, RNG rng, IrisData data) {
AtomicCache<KList<CNG>> cache = biome.getLayerSeaHeightGenerators();
KList<CNG> cached = cache.getIfPresent();
if (cached != null) {
return cached;
}
return cache.aquire(() ->
{
KList<CNG> layerSeaHeightGenerators = new KList<>();
int m = 7735;
for (IrisBiomePaletteLayer i : biome.getSeaLayers()) {
layerSeaHeightGenerators.add(i.getHeightGenerator(rng.nextParallelRNG((m++) * m * m * m), data));
private static void appendLayer(KList<PlatformBlockState> blocks, IrisBiomePaletteLayer layer,
int layerIndex, int thickness, double x, double z,
RNG random, int maxDepth, IrisData data) {
double zoom = layer.getZoom();
for (int offset = 0; offset < thickness && blocks.size() < maxDepth; offset++) {
try {
blocks.add(layer.get(random, layerIndex + offset,
(x + offset) / zoom, offset, (z - offset) / zoom, data));
} catch (Throwable error) {
IrisLogging.reportError(error);
}
return layerSeaHeightGenerators;
});
}
}
static PlatformBlockState getSurfaceBlock(IrisBiome biome, int x, int z, RNG rng, IrisData idm) {