Merge W2C-terrain

This commit is contained in:
Brian Neumann-Fopiano committed 2026-09-28 14:44:12 -04:00
commit 491382abaa
18 files changed
+1183 -395

No files matched your search

@@ -24,7 +24,7 @@ import art.arcane.iris.generation.decoration.IrisDecorator;
import art.arcane.iris.generation.decoration.IrisDepositGenerator;
import art.arcane.iris.generation.decoration.IrisDepositVariant;
import art.arcane.iris.generation.decoration.IrisOreGenerator;
import art.arcane.iris.generation.decoration.IrisOreGeneratorBounds;
import art.arcane.iris.generation.decoration.IrisOreBands;
import art.arcane.iris.generation.decoration.IrisProceduralObjects;
import art.arcane.iris.generation.hydrology.IrisRiverPolicy;
import art.arcane.iris.generation.noise.IrisGeneratorStyle;
@@ -120,8 +120,8 @@ public class IrisBiome extends IrisRegistrant implements Rarity {
private final transient IrisBiomeLayerHeightCache layerSeaHeightGenerators = new IrisBiomeLayerHeightCache();
private final transient AtomicCache<KList<IrisOreGenerator>> surfaceOreCache = new AtomicCache<>();
private final transient AtomicCache<KList<IrisOreGenerator>> undergroundOreCache = new AtomicCache<>();
private final transient AtomicCache<IrisOreGeneratorBounds> surfaceOreBoundsCache = new AtomicCache<>();
private final transient AtomicCache<IrisOreGeneratorBounds> undergroundOreBoundsCache = new AtomicCache<>();
private final transient AtomicCache<IrisOreBands> surfaceOreBandsCache = new AtomicCache<>();
private final transient AtomicCache<IrisOreBands> undergroundOreBandsCache = new AtomicCache<>();
private final transient AtomicCache<KSet<String>> surfaceOreReplaceableBlockData = new AtomicCache<>();
private final transient AtomicCache<EnumMap<IrisDecorationPart, IrisDecorator[]>> decoratorBuckets = new AtomicCache<>();
private final transient AtomicCache<Biome> derivativeResolved = new AtomicCache<>();
@@ -338,8 +338,8 @@ public class IrisBiome extends IrisRegistrant implements Rarity {
this.ores = ores == null ? new KList<>() : ores;
surfaceOreCache.reset();
undergroundOreCache.reset();
surfaceOreBoundsCache.reset();
undergroundOreBoundsCache.reset();
surfaceOreBandsCache.reset();
undergroundOreBandsCache.reset();
}
public IrisBiome setSurfaceOreReplaceableBlocks(KList<String> surfaceOreReplaceableBlocks) {
@@ -348,20 +348,12 @@ public class IrisBiome extends IrisRegistrant implements Rarity {
return this;
}
public KList<IrisOreGenerator> getSurfaceOreGenerators() {
return IrisBiomeOres.getSurfaceOreGenerators(this);
public IrisOreBands getSurfaceOreBands() {
return IrisBiomeOres.getSurfaceOreBands(this);
}
public KList<IrisOreGenerator> getUndergroundOreGenerators() {
return IrisBiomeOres.getUndergroundOreGenerators(this);
}
public IrisOreGeneratorBounds getSurfaceOreGeneratorBounds() {
return IrisBiomeOres.getSurfaceOreGeneratorBounds(this);
}
public IrisOreGeneratorBounds getUndergroundOreGeneratorBounds() {
return IrisBiomeOres.getUndergroundOreGeneratorBounds(this);
public IrisOreBands getUndergroundOreBands() {
return IrisBiomeOres.getUndergroundOreBands(this);
}
public Biome getDerivative() {
@@ -19,7 +19,7 @@
package art.arcane.iris.generation.biome;
import art.arcane.iris.generation.decoration.IrisOreGenerator;
import art.arcane.iris.generation.decoration.IrisOreGeneratorBounds;
import art.arcane.iris.generation.decoration.IrisOreBands;
import art.arcane.iris.generation.decoration.IrisProceduralBlocks;
import art.arcane.iris.pack.loading.IrisData;
@@ -73,30 +73,22 @@ final class IrisBiomeOres {
.contains(IrisProceduralBlocks.materialKey(state));
}
static KList<IrisOreGenerator> getSurfaceOreGenerators(IrisBiome biome) {
return getOres(biome, true);
}
static KList<IrisOreGenerator> getUndergroundOreGenerators(IrisBiome biome) {
return getOres(biome, false);
}
static IrisOreGeneratorBounds getSurfaceOreGeneratorBounds(IrisBiome biome) {
static IrisOreBands getSurfaceOreBands(IrisBiome biome) {
// getIfPresent fast path: aquire allocates a capturing lambda even on a hit, and this
// runs per column on the terrain hot path.
IrisOreGeneratorBounds cached = biome.getSurfaceOreBoundsCache().getIfPresent();
IrisOreBands cached = biome.getSurfaceOreBandsCache().getIfPresent();
if (cached != null) {
return cached;
}
return biome.getSurfaceOreBoundsCache().aquire(() -> IrisOreGeneratorBounds.of(getSurfaceOres(biome)));
return biome.getSurfaceOreBandsCache().aquire(() -> IrisOreBands.of(getSurfaceOres(biome)));
}
static IrisOreGeneratorBounds getUndergroundOreGeneratorBounds(IrisBiome biome) {
IrisOreGeneratorBounds cached = biome.getUndergroundOreBoundsCache().getIfPresent();
static IrisOreBands getUndergroundOreBands(IrisBiome biome) {
IrisOreBands cached = biome.getUndergroundOreBandsCache().getIfPresent();
if (cached != null) {
return cached;
}
return biome.getUndergroundOreBoundsCache().aquire(() -> IrisOreGeneratorBounds.of(getUndergroundOres(biome)));
return biome.getUndergroundOreBandsCache().aquire(() -> IrisOreBands.of(getUndergroundOres(biome)));
}
private static KList<IrisOreGenerator> getSurfaceOres(IrisBiome biome) {
@@ -47,6 +47,7 @@ import lombok.Setter;
import lombok.experimental.Accessors;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;
@Snippet("biome-palette")
@Accessors(chain = true)
@@ -64,7 +65,8 @@ public class IrisBiomePaletteLayer {
new LazyBoundedCache<>(LAYER_GENERATOR_CACHE_SIZE);
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
private final transient AtomicReference<CachedLayerGenerator> recentLayerGenerator = new AtomicReference<>();
private final transient AtomicReferenceArray<CachedLayerGenerator> recentLayerGenerators =
new AtomicReferenceArray<>(LAYER_GENERATOR_CACHE_SIZE);
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
private final transient LazyBoundedCache<LayerGeneratorKey, CNG> heightGenerators =
@@ -145,7 +147,8 @@ public class IrisBiomePaletteLayer {
public CNG getLayerGenerator(RNG parent, int signature, IrisData data) {
Engine engine = data == null ? null : data.getEngine();
long generatorSeed = parent.getSeed() + signature + minHeight + maxHeight + getBlockData(data).size();
CachedLayerGenerator recent = recentLayerGenerator.get();
int slot = (int) generatorSeed & (LAYER_GENERATOR_CACHE_SIZE - 1);
CachedLayerGenerator recent = recentLayerGenerators.getAcquire(slot);
if (recent != null && recent.key.matches(data, engine, generatorSeed)) {
return recent.generator;
}
@@ -154,7 +157,7 @@ public class IrisBiomePaletteLayer {
CNG generator = layerGenerators.computeIfAbsent(key,
ignored -> style.create(new RNG(generatorSeed), data, engine));
if (generator != null) {
recentLayerGenerator.set(new CachedLayerGenerator(key, generator));
recentLayerGenerators.setRelease(slot, new CachedLayerGenerator(key, generator));
}
return generator;
}
@@ -18,20 +18,50 @@
package art.arcane.iris.generation.chunk;
import art.arcane.volmlib.util.function.Consumer4;
import art.arcane.volmlib.util.hunk.Hunk;
import art.arcane.volmlib.util.hunk.view.ListeningHunk;
public final class ColumnExtentListeningHunk<T> extends ListeningHunk<T> implements ColumnExtent {
public final class ColumnExtentListeningHunk<T> implements Hunk<T>, ColumnExtent {
private final Hunk<T> source;
private final Listener<T> listener;
public ColumnExtentListeningHunk(Hunk<T> source, Consumer4<Integer, Integer, Integer, T> listener) {
super(source, listener);
public ColumnExtentListeningHunk(Hunk<T> source, Listener<T> listener) {
this.source = source;
this.listener = listener;
}
@Override
public void setRaw(int x, int y, int z, T t) {
listener.onWrite(x, y, z, t);
source.setRaw(x, y, z, t);
}
@Override
public T getRaw(int x, int y, int z) {
return source.getRaw(x, y, z);
}
@Override
public int getWidth() {
return source.getWidth();
}
@Override
public int getHeight() {
return source.getHeight();
}
@Override
public int getDepth() {
return source.getDepth();
}
@Override
public int highestStoredY(int x, int z) {
return ColumnExtent.highestStoredY(source, x, z);
}
@FunctionalInterface
public interface Listener<T> {
void onWrite(int x, int y, int z, T value);
}
}
@@ -0,0 +1,99 @@
package art.arcane.iris.generation.decoration;
import art.arcane.iris.pack.loading.IrisData;
import art.arcane.iris.pack.value.IrisRange;
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.math.RNG;
import java.util.Arrays;
/**
* One ore list (a biome's, region's or dimension's surface or underground ores) indexed by Y. Each Y
* holds the generators whose range contains it, in list order, so resolving a block tries exactly
* the generators that could place there and still returns the first one that does.
*/
public final class IrisOreBands {
public static final IrisOreBands EMPTY = new IrisOreBands(new IrisOreGenerator[0], 0, -1, null);
private static final int MAXIMUM_INDEXED_HEIGHT = 8192;
private final IrisOreGenerator[] generators;
private final int minY;
private final int maxY;
private final IrisOreGenerator[][] byY;
private IrisOreBands(IrisOreGenerator[] generators, int minY, int maxY, IrisOreGenerator[][] byY) {
this.generators = generators;
this.minY = minY;
this.maxY = maxY;
this.byY = byY;
}
public static IrisOreBands of(KList<IrisOreGenerator> ores) {
if (ores == null || ores.isEmpty()) {
return EMPTY;
}
IrisOreGenerator[] generators = ores.toArray(new IrisOreGenerator[0]);
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for (IrisOreGenerator generator : generators) {
IrisRange range = generator.getRange();
min = Math.min(min, range.getMin());
max = Math.max(max, range.getMax());
}
if (!(min <= max)) {
return EMPTY;
}
int minY = (int) Math.ceil(min);
int maxY = (int) Math.floor(max);
if (minY > maxY) {
return EMPTY;
}
long height = (long) maxY - minY + 1L;
return new IrisOreBands(generators, minY, maxY,
height > MAXIMUM_INDEXED_HEIGHT ? null : index(generators, minY, (int) height));
}
private static IrisOreGenerator[][] index(IrisOreGenerator[] generators, int minY, int height) {
IrisOreGenerator[][] byY = new IrisOreGenerator[height][];
IrisOreGenerator[] scratch = new IrisOreGenerator[generators.length];
IrisOreGenerator[] previous = null;
for (int offset = 0; offset < height; offset++) {
int y = minY + offset;
int count = 0;
for (IrisOreGenerator generator : generators) {
if (generator.getRange().contains(y)) {
scratch[count++] = generator;
}
}
IrisOreGenerator[] candidates = Arrays.copyOf(scratch, count);
byY[offset] = previous != null && Arrays.equals(previous, candidates) ? previous : candidates;
previous = byY[offset];
}
return byY;
}
public boolean hasOres() {
return minY <= maxY;
}
public boolean contains(int y) {
return y >= minY && y <= maxY;
}
/**
* The ore placed at a Y inside {@link #contains(int)}, or null when no generator places one.
*/
public NativeBlockState generate(int x, int y, int z, RNG rng, IrisData data) {
IrisOreGenerator[] candidates = byY == null ? generators : byY[y - minY];
for (IrisOreGenerator candidate : candidates) {
NativeBlockState ore = candidate.generate(x, y, z, rng, data);
if (ore != null) {
return ore;
}
}
return null;
}
}
@@ -55,10 +55,10 @@ public class IrisOreGenerator {
@Description("Vertical band (min, max) this ore can generate in, in engine-local Y where 0 is the bottom of the dimension, not world Y.")
private IrisRange range = new IrisRange(30, 80);
private transient AtomicCache<CNG> chanceCache = new AtomicCache<>();
private transient AtomicCache<OreChance> chanceCache = new AtomicCache<>();
public void warm(RNG rng, IrisData data) {
chanceCache.aquire(() -> chanceStyle.create(rng, data));
chanceCache.aquire(() -> OreChance.of(chanceStyle.create(rng, data)));
palette.getLayerGenerator(rng, data);
}
@@ -71,15 +71,30 @@ public class IrisOreGenerator {
return null;
}
CNG chance = chanceCache.getIfPresent();
OreChance chance = chanceCache.getIfPresent();
if (chance == null) {
chance = chanceCache.aquire(() -> chanceStyle.create(rng, data));
chance = chanceCache.aquire(() -> OreChance.of(chanceStyle.create(rng, data)));
}
if (chance.noise(x, y, z) > threshold) {
if (chance.sample(x, y, z) > threshold) {
return null;
}
return palette.get(rng, x, y, z, data);
}
/**
* The chance field, with its single value resolved once when it is constant (a FLAT style),
* which bands such as a full deepslate layer sample at every block.
*/
private record OreChance(CNG noise, boolean constant, double value) {
private static OreChance of(CNG noise) {
boolean constant = noise.isConstant();
return new OreChance(noise, constant, constant ? noise.noise(0D, 0D, 0D) : 0D);
}
private double sample(int x, int y, int z) {
return constant ? value : noise.noise(x, y, z);
}
}
}
@@ -1,45 +0,0 @@
package art.arcane.iris.generation.decoration;
import art.arcane.iris.pack.value.IrisRange;
import art.arcane.volmlib.util.collection.KList;
public final class IrisOreGeneratorBounds {
public static final IrisOreGeneratorBounds EMPTY = new IrisOreGeneratorBounds(false, 0D, 0D);
private final boolean hasOres;
private final double min;
private final double max;
private IrisOreGeneratorBounds(boolean hasOres, double min, double max) {
this.hasOres = hasOres;
this.min = min;
this.max = max;
}
public static IrisOreGeneratorBounds of(KList<IrisOreGenerator> ores) {
if (ores == null || ores.isEmpty()) {
return EMPTY;
}
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
int oreCount = ores.size();
for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) {
IrisOreGenerator oreGenerator = ores.get(oreIndex);
IrisRange range = oreGenerator.getRange();
min = Math.min(min, range.getMin());
max = Math.max(max, range.getMax());
}
return new IrisOreGeneratorBounds(true, min, max);
}
public boolean hasOres() {
return hasOres;
}
public boolean contains(int y) {
return hasOres && y >= min && y <= max;
}
}
@@ -0,0 +1,53 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2026 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.generation.runtime;
import art.arcane.iris.generation.block.B;
import art.arcane.iris.generation.chunk.ColumnExtentListeningHunk;
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
/**
* Forwards a chunk's block writes to {@link BlockUpdater#catchBlockUpdates} at world coordinates.
* The last state seen to need neither a block update nor custom handling is remembered, so the
* long runs of plain terrain skip the per-block checks. Racing writers only ever store such states.
*/
final class ChunkBlockUpdateListener implements ColumnExtentListeningHunk.Listener<NativeBlockState> {
private final BlockUpdater updater;
private final int originX;
private final int originZ;
private NativeBlockState quiet;
ChunkBlockUpdateListener(BlockUpdater updater, int originX, int originZ) {
this.updater = updater;
this.originX = originX;
this.originZ = originZ;
}
@Override
public void onWrite(int x, int y, int z, NativeBlockState state) {
if (state == null || state == quiet) {
return;
}
if (B.isUpdatable(state) || state.isCustom()) {
updater.catchBlockUpdates(originX + x, y, originZ + z, state);
return;
}
quiet = state;
}
}
@@ -1491,7 +1491,7 @@ public class IrisEngine implements Engine {
IrisContext.Scope generationScope = IrisContext.open(this, lease.sessionId(), null)) {
getEngineData().getStatistics().generatedChunk();
PrecisionStopwatch p = PrecisionStopwatch.start();
Hunk<NativeBlockState> blocks = new ColumnExtentListeningHunk<>(vblocks, (xx, y, zz, t) -> catchBlockUpdates(x + xx, y, z + zz, t));
Hunk<NativeBlockState> blocks = new ColumnExtentListeningHunk<>(vblocks, new ChunkBlockUpdateListener(this, x, z));
if (getDimension().isDebugChunkCrossSections() && ((x >> 4) % getDimension().getDebugCrossSectionsMod() == 0 || (z >> 4) % getDimension().getDebugCrossSectionsMod() == 0)) {
NativeBlockState crossSection = B.getState("CRYING_OBSIDIAN");
@@ -30,8 +30,7 @@ import art.arcane.iris.generation.biome.IrisBiome;
import art.arcane.iris.generation.decoration.IrisProceduralBlocks;
import art.arcane.iris.generation.hydrology.IrisSurfaceRiverBedConfig;
import art.arcane.iris.generation.terrain.IrisDimension;
import art.arcane.iris.generation.decoration.IrisOreGenerator;
import art.arcane.iris.generation.decoration.IrisOreGeneratorBounds;
import art.arcane.iris.generation.decoration.IrisOreBands;
import art.arcane.iris.generation.terrain.IrisRegion;
import art.arcane.iris.generation.hydrology.IrisRiverMaterialConfig;
import art.arcane.iris.generation.hydrology.IrisSurfaceRiverBankConfig;
@@ -52,8 +51,6 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<NativeBloc
private static final BoundBlockState AIR = BoundBlockState.of("AIR");
@Getter
private final RNG rng;
@Getter
private int lastBedrock = -1;
public IrisTerrainNormalActuator(Engine engine) {
super(engine, "Terrain");
@@ -64,263 +61,25 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<NativeBloc
@Override
public void onActuate(int x, int z, Hunk<NativeBlockState> h, boolean multicore, ChunkContext context) {
PrecisionStopwatch p = PrecisionStopwatch.start();
for (int xf = 0; xf < h.getWidth(); xf++) {
terrainSliver(x, z, xf, h, context);
}
paint(x, z, h, context);
getEngine().getMetrics().getTerrain().put(p.getMilliseconds());
}
/**
* This is calling 1/16th of a chunk x/z slice. It is a plane from sky to bedrock 1 thick in the x direction.
* Paints every column of the chunk from its top down to bedrock. Columns are walked z-major so
* consecutive columns share the cache lines of the x-minor block buffer.
*
* @param x the chunk x in blocks
* @param z the chunk z in blocks
* @param xf the current x slice
* @param h the blockdata
* @param x the chunk x in blocks
* @param z the chunk z in blocks
*/
@BlockCoordinates
public void terrainSliver(int x, int z, int xf, Hunk<NativeBlockState> h, ChunkContext context) {
int chunkHeight = h.getHeight();
int chunkDepth = h.getDepth();
IrisDimension dimension = getDimension();
IrisData data = getData();
IrisComplex complex = getComplex();
ProceduralStream<Double> riverWaterSurfaceStream = complex.getRiverWaterSurfaceStream();
RNG localRng = rng;
boolean bedrockEnabled = dimension.isBedrock();
boolean hideOres = dimension.isHideOresForHiddenOre();
ChunkedDataCache<IrisBiome> biomeCache = context.getBiome();
ChunkedDataCache<IrisRegion> regionCache = context.getRegion();
ChunkedDataCache<NativeBlockState> rockCache = context.getRock();
int realX = xf + x;
UpperDimensionContext upperContext = getEngine().getUpperContext();
// Dimension-level ore lookups are chunk-invariant; resolving them per column paid
// four accessor chains times 256 per chunk for constants.
KList<IrisOreGenerator> dimensionSurfaceOres = hideOres ? null : dimension.getSurfaceOreGenerators();
KList<IrisOreGenerator> dimensionUndergroundOres = hideOres ? null : dimension.getUndergroundOreGenerators();
IrisOreGeneratorBounds dimensionSurfaceOreBounds = hideOres ? IrisOreGeneratorBounds.EMPTY : dimension.getSurfaceOreGeneratorBounds();
IrisOreGeneratorBounds dimensionUndergroundOreBounds = hideOres ? IrisOreGeneratorBounds.EMPTY : dimension.getUndergroundOreGeneratorBounds();
IrisSurfaceRiverBankConfig riverBanks = dimension.getHydrology() == null
? null
: dimension.getHydrology().getRivers().getSurface().getBanks();
boolean exposeCutStrata = riverBanks != null && riverBanks.isExposeCutStrata();
IrisSurfaceRiverBedConfig riverBed = dimension.getHydrology() == null
? null
: dimension.getHydrology().getRivers().getSurface().getBed();
boolean padRiverBed = riverBed != null && !riverBed.isAllowGravityBlocks();
IrisRiverMaterialConfig bedMaterial = riverBed == null ? null : riverBed.getMaterial();
IrisRiverMaterialConfig shoreMaterial = riverBanks == null ? null : riverBanks.getShoreMaterial();
IrisRiverMaterialConfig bankMaterial = riverBanks == null ? null : riverBanks.getBankMaterial();
for (int zf = 0; zf < chunkDepth; zf++) {
int realZ = zf + z;
IrisBiome biome = biomeCache.get(xf, zf);
IrisRegion region = regionCache.get(xf, zf);
int he = Math.min(chunkHeight, context.getRoundedHeight(xf, zf));
int surfaceFluidHeight = Math.min(
chunkHeight,
(int) Math.round(riverWaterSurfaceStream.getDouble(realX, realZ))
);
int hf = Math.max(surfaceFluidHeight, he);
if (hf < 0) {
continue;
}
int topY = Math.min(hf, chunkHeight - 1);
HydrologyColumnSample hydrology = complex.sampleHydrologyColumn(realX, realZ);
HydrologyColumnLayer hydrologyFluid = hydrology == null
? null
: hydrology.primarySurfaceFluidLayerOrNull();
HydrologyColumnLayer hydrologyTerrain = hydrology == null
? null
: hydrology.primarySurfaceLayerOrNull();
Terrain3DColumn terrainColumn = complex.terrainColumn(realX, realZ, hydrology);
int terrainSpan = terrainColumn == null ? -1 : terrainColumn.spanCount() - 1;
int layerSurfaceY = he;
int layerCeilingY = 0;
boolean exposeRiverStrata = exposeCutStrata
&& hydrologyTerrain != null
&& hydrologyTerrain.terrainOwned()
&& !hydrologyTerrain.channel();
Terrain3DColumn naturalColumn = exposeRiverStrata ? complex.naturalTerrainColumn(realX, realZ) : null;
int cut = 0;
boolean riverOwned = padRiverBed && hydrologyTerrain != null && hydrologyTerrain.terrainOwned();
IrisRiverMaterialConfig roleMaterial = hydrologyRoleMaterial(
hydrologyTerrain, bedMaterial, shoreMaterial, bankMaterial);
NativeBlockState fluid = hydrologyFluid == null || complex.getTransitionDisplacement() != null
? complex.resolveSurfaceFluid(realX, realZ)
: complex.resolveHydrologyFluid(hydrologyFluid.profileKey(), realX, realZ);
NativeBlockState rock = rockCache.get(xf, zf);
NativeBlockState mappedSurfaceBlock = complex.getImageMapRuntime().sampleSurfaceBlock(realX, realZ);
KList<IrisOreGenerator> biomeSurfaceOres = hideOres ? null : biome.getSurfaceOreGenerators();
KList<IrisOreGenerator> regionSurfaceOres = hideOres ? null : region.getSurfaceOreGenerators();
KList<IrisOreGenerator> biomeUndergroundOres = hideOres ? null : biome.getUndergroundOreGenerators();
KList<IrisOreGenerator> regionUndergroundOres = hideOres ? null : region.getUndergroundOreGenerators();
IrisOreGeneratorBounds biomeSurfaceOreBounds = hideOres ? IrisOreGeneratorBounds.EMPTY : biome.getSurfaceOreGeneratorBounds();
IrisOreGeneratorBounds regionSurfaceOreBounds = hideOres ? IrisOreGeneratorBounds.EMPTY : region.getSurfaceOreGeneratorBounds();
IrisOreGeneratorBounds biomeUndergroundOreBounds = hideOres ? IrisOreGeneratorBounds.EMPTY : biome.getUndergroundOreGeneratorBounds();
IrisOreGeneratorBounds regionUndergroundOreBounds = hideOres ? IrisOreGeneratorBounds.EMPTY : region.getUndergroundOreGeneratorBounds();
boolean hasSurfaceOres = biomeSurfaceOreBounds.hasOres() || regionSurfaceOreBounds.hasOres() || dimensionSurfaceOreBounds.hasOres();
boolean hasUndergroundOres = biomeUndergroundOreBounds.hasOres() || regionUndergroundOreBounds.hasOres() || dimensionUndergroundOreBounds.hasOres();
KList<NativeBlockState> blocks = null;
KList<NativeBlockState> ceilingBlocks = null;
KList<NativeBlockState> fblocks = null;
for (int i = topY; i >= 0; i--) {
if (i == 0 && bedrockEnabled) {
h.setRaw(xf, i, zf, BEDROCK.get());
lastBedrock = i;
continue;
}
if (terrainColumn != null && i <= he) {
while (terrainSpan >= 0 && i < terrainColumn.ceiling(terrainSpan)) {
terrainSpan--;
blocks = null;
ceilingBlocks = null;
}
if (terrainSpan < 0 || i > terrainColumn.floor(terrainSpan)) {
h.setRaw(xf, i, zf, AIR.get());
continue;
}
layerSurfaceY = terrainColumn.floor(terrainSpan);
layerCeilingY = terrainColumn.ceiling(terrainSpan);
}
NativeBlockState ore = null;
if (hasSurfaceOres) {
if (biomeSurfaceOreBounds.contains(i)) {
ore = generateOres(biomeSurfaceOres, realX, i, realZ, localRng, data);
}
if (ore == null && regionSurfaceOreBounds.contains(i)) {
ore = generateOres(regionSurfaceOres, realX, i, realZ, localRng, data);
}
if (ore == null && dimensionSurfaceOreBounds.contains(i)) {
ore = generateOres(dimensionSurfaceOres, realX, i, realZ, localRng, data);
}
}
if (ore != null) {
h.setRaw(xf, i, zf, ore);
continue;
}
if (i > he && i <= hf) {
int fdepth = hf - i;
if (hydrologyFluid == null && fblocks == null) {
fblocks = biome.generateSeaLayers(realX, realZ, localRng, hf - he, data);
}
h.setRaw(xf, i, zf, HydrologyFluidLayerSelector.select(
fblocks,
fdepth,
fluid,
hydrologyFluid != null
));
continue;
}
if (i <= he) {
int depth = layerSurfaceY - i;
if (depth == 0 && mappedSurfaceBlock != null) {
h.setRaw(xf, i, zf, mappedSurfaceBlock);
continue;
}
if (blocks == null) {
if (exposeRiverStrata) {
int naturalSurfaceY = naturalColumn == null ? hydrology.naturalHeight() : naturalColumn.surfaceY(layerSurfaceY);
cut = Math.max(0, naturalSurfaceY - layerSurfaceY);
}
blocks = biome.generateLayers(dimension, realX, realZ, localRng,
layerSurfaceY + cut, layerSurfaceY + cut, data, complex);
}
if (layerCeilingY > 0 && depth >= 2 && i - layerCeilingY < 2) {
if (ceilingBlocks == null) {
ceilingBlocks = biome.generateCeilingLayers(dimension, realX, realZ, localRng,
2, layerCeilingY, data, complex);
}
int ceilingDepth = i - layerCeilingY;
h.setRaw(xf, i, zf, ceilingBlocks.hasIndex(ceilingDepth)
? ceilingBlocks.get(ceilingDepth) : rock);
continue;
}
int strataIndex = strataIndex(depth, cut, blocks.size());
NativeBlockState layerBlock = paintHydrologyMaterial(
blocks.hasIndex(strataIndex) ? blocks.get(strataIndex) : null,
roleMaterial, depth, localRng, realX, i, realZ, data);
if (layerBlock != null) {
if (riverOwned && depth <= riverBed.getPadding() && IrisProceduralBlocks.isGravityAffected(layerBlock)) {
layerBlock = riverBed.getPaddingPalette().get(localRng, realX, i, realZ, data);
}
h.setRaw(xf, i, zf, layerBlock);
continue;
}
if (hasUndergroundOres) {
if (biomeUndergroundOreBounds.contains(i)) {
ore = generateOres(biomeUndergroundOres, realX, i, realZ, localRng, data);
}
if (ore == null && regionUndergroundOreBounds.contains(i)) {
ore = generateOres(regionUndergroundOres, realX, i, realZ, localRng, data);
}
if (ore == null && dimensionUndergroundOreBounds.contains(i)) {
ore = generateOres(dimensionUndergroundOres, realX, i, realZ, localRng, data);
}
}
if (ore != null) {
h.setRaw(xf, i, zf, ore);
} else {
h.setRaw(xf, i, zf, rock);
}
}
}
if (upperContext != null) {
UpperDimensionContext.Column upperColumn = upperContext.sampleColumn(realX, realZ);
int upperSurfaceY = upperColumn.surfaceY();
if (upperSurfaceY < chunkHeight - 1) {
IrisBiome upperBiome = upperContext.getUpperBiome(realX, realZ);
NativeBlockState upperRock = upperContext.getRockBlock(realX, realZ);
NativeBlockState upperMappedSurface = upperContext.getSurfaceBlock(realX, realZ);
KList<NativeBlockState> upperBlocks = null;
int paletteSourceY = -1;
for (int y = chunkHeight - 1; y >= upperSurfaceY; y--) {
if (y == chunkHeight - 1 && bedrockEnabled) {
h.setRaw(xf, y, zf, BEDROCK.get());
continue;
}
if (!upperColumn.isSolid(y)) {
h.setRaw(xf, y, zf, AIR.get());
continue;
}
int faceY = upperColumn.faceY(y);
int sourceSurfaceY = upperColumn.height() - 1 - faceY;
if (sourceSurfaceY != paletteSourceY) {
paletteSourceY = sourceSurfaceY;
upperBlocks = null;
}
if (y == faceY && upperMappedSurface != null) {
h.setRaw(xf, y, zf, upperMappedSurface);
continue;
}
int depthFromFace = y - faceY;
if (upperBlocks == null && upperBiome != null) {
upperBlocks = upperBiome.generateLayersWithSlope(upperContext.getDimension(),
realX, realZ, localRng, sourceSurfaceY, sourceSurfaceY,
upperContext.getData(), upperContext.getSurfaceSlopeStream(sourceSurfaceY));
}
if (upperBlocks != null && upperBlocks.hasIndex(depthFromFace)) {
h.setRaw(xf, y, zf, upperBlocks.get(depthFromFace));
} else {
h.setRaw(xf, y, zf, upperRock);
}
}
}
public void paint(int x, int z, Hunk<NativeBlockState> h, ChunkContext context) {
ColumnPainter painter = new ColumnPainter(x, z, h, context);
int width = h.getWidth();
int depth = h.getDepth();
for (int zf = 0; zf < depth; zf++) {
for (int xf = 0; xf < width; xf++) {
painter.paint(xf, zf);
}
}
}
@@ -385,20 +144,307 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<NativeBloc
return painted == null ? layerBlock : painted;
}
private NativeBlockState generateOres(KList<IrisOreGenerator> oreGenerators, int x, int y, int z, RNG rng, IrisData data) {
if (oreGenerators == null || oreGenerators.isEmpty()) {
return null;
private final class ColumnPainter {
private final int x;
private final int z;
private final Hunk<NativeBlockState> h;
private final ChunkContext context;
private final int chunkHeight;
private final IrisDimension dimension;
private final IrisData data;
private final IrisComplex complex;
private final ProceduralStream<Double> riverWaterSurfaceStream;
private final boolean bedrockEnabled;
private final boolean hideOres;
private final boolean transitionDisplaced;
private final ChunkedDataCache<IrisBiome> biomeCache;
private final ChunkedDataCache<IrisRegion> regionCache;
private final ChunkedDataCache<NativeBlockState> rockCache;
private final UpperDimensionContext upperContext;
private final IrisOreBands dimensionSurfaceOres;
private final IrisOreBands dimensionUndergroundOres;
private final boolean exposeCutStrata;
private final IrisSurfaceRiverBedConfig riverBed;
private final boolean padRiverBed;
private final IrisRiverMaterialConfig bedMaterial;
private final IrisRiverMaterialConfig shoreMaterial;
private final IrisRiverMaterialConfig bankMaterial;
private final NativeBlockState air;
private final NativeBlockState bedrock;
private IrisOreBands biomeSurfaceOres;
private IrisOreBands regionSurfaceOres;
private IrisOreBands biomeUndergroundOres;
private IrisOreBands regionUndergroundOres;
private ColumnPainter(int x, int z, Hunk<NativeBlockState> h, ChunkContext context) {
this.x = x;
this.z = z;
this.h = h;
this.context = context;
chunkHeight = h.getHeight();
dimension = getDimension();
data = getData();
complex = getComplex();
riverWaterSurfaceStream = complex.getRiverWaterSurfaceStream();
bedrockEnabled = dimension.isBedrock();
hideOres = dimension.isHideOresForHiddenOre();
transitionDisplaced = complex.getTransitionDisplacement() != null;
biomeCache = context.getBiome();
regionCache = context.getRegion();
rockCache = context.getRock();
upperContext = getEngine().getUpperContext();
dimensionSurfaceOres = hideOres ? IrisOreBands.EMPTY : dimension.getSurfaceOreBands();
dimensionUndergroundOres = hideOres ? IrisOreBands.EMPTY : dimension.getUndergroundOreBands();
IrisSurfaceRiverBankConfig riverBanks = dimension.getHydrology() == null
? null
: dimension.getHydrology().getRivers().getSurface().getBanks();
exposeCutStrata = riverBanks != null && riverBanks.isExposeCutStrata();
riverBed = dimension.getHydrology() == null
? null
: dimension.getHydrology().getRivers().getSurface().getBed();
padRiverBed = riverBed != null && !riverBed.isAllowGravityBlocks();
bedMaterial = riverBed == null ? null : riverBed.getMaterial();
shoreMaterial = riverBanks == null ? null : riverBanks.getShoreMaterial();
bankMaterial = riverBanks == null ? null : riverBanks.getBankMaterial();
air = AIR.get();
bedrock = BEDROCK.get();
}
int oreCount = oreGenerators.size();
for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) {
IrisOreGenerator oreGenerator = oreGenerators.get(oreIndex);
NativeBlockState ore = oreGenerator.generate(x, y, z, rng, data);
if (ore != null) {
return ore;
private void paint(int xf, int zf) {
int realX = xf + x;
int realZ = zf + z;
IrisBiome biome = biomeCache.get(xf, zf);
IrisRegion region = regionCache.get(xf, zf);
int he = Math.min(chunkHeight, context.getRoundedHeight(xf, zf));
int surfaceFluidHeight = Math.min(
chunkHeight,
(int) Math.round(riverWaterSurfaceStream.getDouble(realX, realZ))
);
int hf = Math.max(surfaceFluidHeight, he);
if (hf < 0) {
return;
}
int topY = Math.min(hf, chunkHeight - 1);
HydrologyColumnSample hydrology = complex.sampleHydrologyColumn(realX, realZ);
HydrologyColumnLayer hydrologyFluid = hydrology == null
? null
: hydrology.primarySurfaceFluidLayerOrNull();
HydrologyColumnLayer hydrologyTerrain = hydrology == null
? null
: hydrology.primarySurfaceLayerOrNull();
Terrain3DColumn terrainColumn = complex.terrainColumn(realX, realZ, hydrology);
int terrainSpan = terrainColumn == null ? -1 : terrainColumn.spanCount() - 1;
int layerSurfaceY = he;
int layerCeilingY = 0;
boolean exposeRiverStrata = exposeCutStrata
&& hydrologyTerrain != null
&& hydrologyTerrain.terrainOwned()
&& !hydrologyTerrain.channel();
Terrain3DColumn naturalColumn = exposeRiverStrata ? complex.naturalTerrainColumn(realX, realZ) : null;
int cut = 0;
boolean riverOwned = padRiverBed && hydrologyTerrain != null && hydrologyTerrain.terrainOwned();
IrisRiverMaterialConfig roleMaterial = hydrologyRoleMaterial(
hydrologyTerrain, bedMaterial, shoreMaterial, bankMaterial);
int paintDepth = roleMaterial == null ? 0 : roleMaterial.getDepth();
NativeBlockState fluid = hydrologyFluid == null || transitionDisplaced
? complex.resolveSurfaceFluid(realX, realZ)
: complex.resolveHydrologyFluid(hydrologyFluid.profileKey(), realX, realZ);
NativeBlockState rock = rockCache.get(xf, zf);
NativeBlockState mappedSurfaceBlock = complex.getImageMapRuntime().sampleSurfaceBlock(realX, realZ);
biomeSurfaceOres = hideOres ? IrisOreBands.EMPTY : biome.getSurfaceOreBands();
regionSurfaceOres = hideOres ? IrisOreBands.EMPTY : region.getSurfaceOreBands();
biomeUndergroundOres = hideOres ? IrisOreBands.EMPTY : biome.getUndergroundOreBands();
regionUndergroundOres = hideOres ? IrisOreBands.EMPTY : region.getUndergroundOreBands();
boolean hasSurfaceOres = biomeSurfaceOres.hasOres() || regionSurfaceOres.hasOres() || dimensionSurfaceOres.hasOres();
boolean hasUndergroundOres = biomeUndergroundOres.hasOres() || regionUndergroundOres.hasOres() || dimensionUndergroundOres.hasOres();
int bedrockFloor = bedrockEnabled ? 1 : 0;
KList<NativeBlockState> blocks = null;
KList<NativeBlockState> ceilingBlocks = null;
KList<NativeBlockState> fblocks = null;
for (int i = topY; i >= 0; i--) {
if (i == 0 && bedrockEnabled) {
h.setRaw(xf, i, zf, bedrock);
continue;
}
if (terrainColumn != null && i <= he) {
while (terrainSpan >= 0 && i < terrainColumn.ceiling(terrainSpan)) {
terrainSpan--;
blocks = null;
ceilingBlocks = null;
}
if (terrainSpan < 0 || i > terrainColumn.floor(terrainSpan)) {
h.setRaw(xf, i, zf, air);
continue;
}
layerSurfaceY = terrainColumn.floor(terrainSpan);
layerCeilingY = terrainColumn.ceiling(terrainSpan);
}
NativeBlockState ore = hasSurfaceOres ? surfaceOre(realX, i, realZ) : null;
if (ore != null) {
h.setRaw(xf, i, zf, ore);
continue;
}
if (i > he && i <= hf) {
int fdepth = hf - i;
if (hydrologyFluid == null && fblocks == null) {
fblocks = biome.generateSeaLayers(realX, realZ, rng, hf - he, data);
}
h.setRaw(xf, i, zf, HydrologyFluidLayerSelector.select(
fblocks,
fdepth,
fluid,
hydrologyFluid != null
));
continue;
}
if (i <= he) {
int depth = layerSurfaceY - i;
if (depth == 0 && mappedSurfaceBlock != null) {
h.setRaw(xf, i, zf, mappedSurfaceBlock);
continue;
}
if (blocks == null) {
if (exposeRiverStrata) {
int naturalSurfaceY = naturalColumn == null ? hydrology.naturalHeight() : naturalColumn.surfaceY(layerSurfaceY);
cut = Math.max(0, naturalSurfaceY - layerSurfaceY);
}
blocks = biome.generateLayers(dimension, realX, realZ, rng,
layerSurfaceY + cut, layerSurfaceY + cut, data, complex);
}
int deepFloor = Math.max(bedrockFloor, layerCeilingY > 0 ? layerCeilingY + 2 : 0);
if (i >= deepFloor && depth >= Math.max(blocks.size(), paintDepth)) {
paintDeep(xf, zf, realX, realZ, i, deepFloor, rock, hasSurfaceOres, hasUndergroundOres);
i = deepFloor;
continue;
}
if (layerCeilingY > 0 && depth >= 2 && i - layerCeilingY < 2) {
if (ceilingBlocks == null) {
ceilingBlocks = biome.generateCeilingLayers(dimension, realX, realZ, rng,
2, layerCeilingY, data, complex);
}
int ceilingDepth = i - layerCeilingY;
h.setRaw(xf, i, zf, ceilingBlocks.hasIndex(ceilingDepth)
? ceilingBlocks.get(ceilingDepth) : rock);
continue;
}
int strataIndex = strataIndex(depth, cut, blocks.size());
NativeBlockState layerBlock = paintHydrologyMaterial(
blocks.hasIndex(strataIndex) ? blocks.get(strataIndex) : null,
roleMaterial, depth, rng, realX, i, realZ, data);
if (layerBlock != null) {
if (riverOwned && depth <= riverBed.getPadding() && IrisProceduralBlocks.isGravityAffected(layerBlock)) {
layerBlock = riverBed.getPaddingPalette().get(rng, realX, i, realZ, data);
}
h.setRaw(xf, i, zf, layerBlock);
continue;
}
ore = hasUndergroundOres ? undergroundOre(realX, i, realZ) : null;
h.setRaw(xf, i, zf, ore != null ? ore : rock);
}
}
if (upperContext != null) {
paintUpper(xf, zf, realX, realZ);
}
}
return null;
/**
* Paints the solid run from {@code top} down to {@code floor}, which lies below every biome
* layer, river material, mapped surface and cave ceiling of its span: only ores or rock land there.
*/
private void paintDeep(int xf, int zf, int realX, int realZ, int top, int floor, NativeBlockState rock,
boolean hasSurfaceOres, boolean hasUndergroundOres) {
for (int y = top; y >= floor; y--) {
NativeBlockState ore = hasSurfaceOres ? surfaceOre(realX, y, realZ) : null;
if (ore == null && hasUndergroundOres) {
ore = undergroundOre(realX, y, realZ);
}
h.setRaw(xf, y, zf, ore != null ? ore : rock);
}
}
private NativeBlockState surfaceOre(int realX, int y, int realZ) {
NativeBlockState ore = null;
if (biomeSurfaceOres.contains(y)) {
ore = biomeSurfaceOres.generate(realX, y, realZ, rng, data);
}
if (ore == null && regionSurfaceOres.contains(y)) {
ore = regionSurfaceOres.generate(realX, y, realZ, rng, data);
}
if (ore == null && dimensionSurfaceOres.contains(y)) {
ore = dimensionSurfaceOres.generate(realX, y, realZ, rng, data);
}
return ore;
}
private NativeBlockState undergroundOre(int realX, int y, int realZ) {
NativeBlockState ore = null;
if (biomeUndergroundOres.contains(y)) {
ore = biomeUndergroundOres.generate(realX, y, realZ, rng, data);
}
if (ore == null && regionUndergroundOres.contains(y)) {
ore = regionUndergroundOres.generate(realX, y, realZ, rng, data);
}
if (ore == null && dimensionUndergroundOres.contains(y)) {
ore = dimensionUndergroundOres.generate(realX, y, realZ, rng, data);
}
return ore;
}
private void paintUpper(int xf, int zf, int realX, int realZ) {
UpperDimensionContext.Column upperColumn = upperContext.sampleColumn(realX, realZ);
int upperSurfaceY = upperColumn.surfaceY();
if (upperSurfaceY >= chunkHeight - 1) {
return;
}
IrisBiome upperBiome = upperContext.getUpperBiome(realX, realZ);
NativeBlockState upperRock = upperContext.getRockBlock(realX, realZ);
NativeBlockState upperMappedSurface = upperContext.getSurfaceBlock(realX, realZ);
KList<NativeBlockState> upperBlocks = null;
int paletteSourceY = -1;
for (int y = chunkHeight - 1; y >= upperSurfaceY; y--) {
if (y == chunkHeight - 1 && bedrockEnabled) {
h.setRaw(xf, y, zf, bedrock);
continue;
}
if (!upperColumn.isSolid(y)) {
h.setRaw(xf, y, zf, air);
continue;
}
int faceY = upperColumn.faceY(y);
int sourceSurfaceY = upperColumn.height() - 1 - faceY;
if (sourceSurfaceY != paletteSourceY) {
paletteSourceY = sourceSurfaceY;
upperBlocks = null;
}
if (y == faceY && upperMappedSurface != null) {
h.setRaw(xf, y, zf, upperMappedSurface);
continue;
}
int depthFromFace = y - faceY;
if (upperBlocks == null && upperBiome != null) {
upperBlocks = upperBiome.generateLayersWithSlope(upperContext.getDimension(),
realX, realZ, rng, sourceSurfaceY, sourceSurfaceY,
upperContext.getData(), upperContext.getSurfaceSlopeStream(sourceSurfaceY));
}
if (upperBlocks != null && upperBlocks.hasIndex(depthFromFace)) {
h.setRaw(xf, y, zf, upperBlocks.get(depthFromFace));
} else {
h.setRaw(xf, y, zf, upperRock);
}
}
}
}
}
@@ -27,7 +27,7 @@ import art.arcane.iris.generation.cave.IrisCaveProfile;
import art.arcane.iris.generation.decoration.IrisDepositGenerator;
import art.arcane.iris.generation.decoration.IrisDepositVariant;
import art.arcane.iris.generation.decoration.IrisOreGenerator;
import art.arcane.iris.generation.decoration.IrisOreGeneratorBounds;
import art.arcane.iris.generation.decoration.IrisOreBands;
import art.arcane.iris.generation.decoration.tree.IrisTreeSettings;
import art.arcane.iris.generation.hydrology.IrisDeepFluidConfig;
import art.arcane.iris.generation.hydrology.IrisHydrology;
@@ -137,8 +137,8 @@ public class IrisDimension extends IrisRegistrant {
private final transient AtomicCache<Map<String, IrisDimensionCarvingEntry>> carvingEntryIndex = new AtomicCache<>();
private final transient AtomicCache<KList<IrisOreGenerator>> surfaceOreCache = new AtomicCache<>();
private final transient AtomicCache<KList<IrisOreGenerator>> undergroundOreCache = new AtomicCache<>();
private final transient AtomicCache<IrisOreGeneratorBounds> surfaceOreBoundsCache = new AtomicCache<>();
private final transient AtomicCache<IrisOreGeneratorBounds> undergroundOreBoundsCache = new AtomicCache<>();
private final transient AtomicCache<IrisOreBands> surfaceOreBandsCache = new AtomicCache<>();
private final transient AtomicCache<IrisOreBands> undergroundOreBandsCache = new AtomicCache<>();
@MinNumber(2)
@Required
@Description("The human readable name of this dimension")
@@ -484,24 +484,16 @@ public class IrisDimension extends IrisRegistrant {
this.ores = ores == null ? new KList<>() : ores;
surfaceOreCache.reset();
undergroundOreCache.reset();
surfaceOreBoundsCache.reset();
undergroundOreBoundsCache.reset();
surfaceOreBandsCache.reset();
undergroundOreBandsCache.reset();
}
public KList<IrisOreGenerator> getSurfaceOreGenerators() {
return getOres(true);
public IrisOreBands getSurfaceOreBands() {
return surfaceOreBandsCache.aquire(() -> IrisOreBands.of(getSurfaceOres()));
}
public KList<IrisOreGenerator> getUndergroundOreGenerators() {
return getOres(false);
}
public IrisOreGeneratorBounds getSurfaceOreGeneratorBounds() {
return surfaceOreBoundsCache.aquire(() -> IrisOreGeneratorBounds.of(getSurfaceOres()));
}
public IrisOreGeneratorBounds getUndergroundOreGeneratorBounds() {
return undergroundOreBoundsCache.aquire(() -> IrisOreGeneratorBounds.of(getUndergroundOres()));
public IrisOreBands getUndergroundOreBands() {
return undergroundOreBandsCache.aquire(() -> IrisOreBands.of(getUndergroundOres()));
}
private KList<IrisOreGenerator> getSurfaceOres() {
@@ -23,7 +23,7 @@ import art.arcane.iris.generation.cave.IrisCaveProfile;
import art.arcane.iris.generation.decoration.IrisDepositGenerator;
import art.arcane.iris.generation.decoration.IrisDepositVariant;
import art.arcane.iris.generation.decoration.IrisOreGenerator;
import art.arcane.iris.generation.decoration.IrisOreGeneratorBounds;
import art.arcane.iris.generation.decoration.IrisOreBands;
import art.arcane.iris.generation.decoration.IrisProceduralObjects;
import art.arcane.iris.generation.hydrology.IrisRiverPolicy;
import art.arcane.iris.pack.validation.CompatPools;
@@ -88,8 +88,8 @@ public class IrisRegion extends IrisRegistrant implements Rarity {
private final transient AtomicCache<Color> cacheColor = new AtomicCache<>();
private final transient AtomicCache<KList<IrisOreGenerator>> surfaceOreCache = new AtomicCache<>();
private final transient AtomicCache<KList<IrisOreGenerator>> undergroundOreCache = new AtomicCache<>();
private final transient AtomicCache<IrisOreGeneratorBounds> surfaceOreBoundsCache = new AtomicCache<>();
private final transient AtomicCache<IrisOreGeneratorBounds> undergroundOreBoundsCache = new AtomicCache<>();
private final transient AtomicCache<IrisOreBands> surfaceOreBandsCache = new AtomicCache<>();
private final transient AtomicCache<IrisOreBands> undergroundOreBandsCache = new AtomicCache<>();
@MinNumber(2)
@Required
@Description("The name of the region")
@@ -217,24 +217,16 @@ public class IrisRegion extends IrisRegistrant implements Rarity {
this.ores = ores == null ? new KList<>() : ores;
surfaceOreCache.reset();
undergroundOreCache.reset();
surfaceOreBoundsCache.reset();
undergroundOreBoundsCache.reset();
surfaceOreBandsCache.reset();
undergroundOreBandsCache.reset();
}
public KList<IrisOreGenerator> getSurfaceOreGenerators() {
return getOres(true);
public IrisOreBands getSurfaceOreBands() {
return surfaceOreBandsCache.aquire(() -> IrisOreBands.of(getSurfaceOres()));
}
public KList<IrisOreGenerator> getUndergroundOreGenerators() {
return getOres(false);
}
public IrisOreGeneratorBounds getSurfaceOreGeneratorBounds() {
return surfaceOreBoundsCache.aquire(() -> IrisOreGeneratorBounds.of(getSurfaceOres()));
}
public IrisOreGeneratorBounds getUndergroundOreGeneratorBounds() {
return undergroundOreBoundsCache.aquire(() -> IrisOreGeneratorBounds.of(getUndergroundOres()));
public IrisOreBands getUndergroundOreBands() {
return undergroundOreBandsCache.aquire(() -> IrisOreBands.of(getUndergroundOres()));
}
private KList<IrisOreGenerator> getSurfaceOres() {