mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-23 16:44:06 +00:00
Unboxed biome selection
- Rarity selection streams pick from precomputed cumulative tables - Implode and surface biome fixes take primitive coordinates - Child selection reuses one derived child seed instead of a new RNG per level - Base biome selection reads bridge and inferred streams directly
This commit is contained in:
1 parent
1acda886b3
commit
7408d02bbe
6 files changed
+113
-29
No files matched your search
@@ -515,7 +515,10 @@ public class IrisBiome extends IrisRegistrant implements Rarity {
|
||||
}
|
||||
|
||||
public CNG getChildrenGenerator(RNG random, int sig, double scale) {
|
||||
long seed = deriveChildStyleSeed(random, sig).getSeed();
|
||||
return getChildrenGenerator(deriveChildStyleSeed(random, sig).getSeed(), scale);
|
||||
}
|
||||
|
||||
public CNG getChildrenGenerator(long seed, double scale) {
|
||||
IrisData data = getLoader();
|
||||
Engine engine = data == null ? null : data.getEngine();
|
||||
CachedChildGenerator recent = recentChildGenerator;
|
||||
@@ -533,6 +536,10 @@ public class IrisBiome extends IrisRegistrant implements Rarity {
|
||||
return random.nextParallelRNG(signature * CHILDREN_SEED_SALT);
|
||||
}
|
||||
|
||||
public static long childStyleSeed(RNG random, int signature) {
|
||||
return deriveChildStyleSeed(random, signature).getSeed();
|
||||
}
|
||||
|
||||
public KList<NativeBlockState> generateLayers(IrisDimension dim, double wx, double wz, RNG random, int maxDepth, int height, IrisData rdata, IrisComplex complex) {
|
||||
return IrisBiomeLayerGenerator.generateLayers(this, dim, wx, wz, random, maxDepth, height, rdata, complex);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import art.arcane.iris.generation.stream.BakedDoubleStream;
|
||||
import art.arcane.iris.generation.stream.BakedHeightStream;
|
||||
import art.arcane.iris.generation.stream.BakedStream;
|
||||
import art.arcane.iris.generation.stream.GenerationStreams;
|
||||
import art.arcane.iris.generation.stream.RarityStream;
|
||||
import art.arcane.iris.generation.stream.CachedDoubleStream2D;
|
||||
import art.arcane.iris.generation.stream.CachedStream2D;
|
||||
import art.arcane.iris.generation.stream.ProvisionalSampling;
|
||||
@@ -132,8 +133,8 @@ import java.nio.file.Path;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(exclude = {"data", "gridBoundsCache", "sharedCornerBounds", "terrainProgram", "bakeCache", "sparseBaseHeights", "biomeRegistry", "regionRegistry", "blockRegistry", "regionSource", "baseBiomeSource", "caveSource", "rockSource", "fluidSource", "naturalHeightSource", "naturalTrueBiomeSource", "trueBiomeSource", "frozenInterpolators", "frozenGenerators", "inferredBiomeStreams", "hydrologyRuntime", "imageMapRuntime", "terrainEngine", "terrain3D", "hydrologyBanks3D"})
|
||||
@ToString(exclude = {"data", "gridBoundsCache", "sharedCornerBounds", "terrainProgram", "bakeCache", "sparseBaseHeights", "biomeRegistry", "regionRegistry", "blockRegistry", "regionSource", "baseBiomeSource", "caveSource", "rockSource", "fluidSource", "naturalHeightSource", "naturalTrueBiomeSource", "trueBiomeSource", "frozenInterpolators", "frozenGenerators", "inferredBiomeStreams", "hydrologyRuntime", "imageMapRuntime", "terrainEngine", "terrain3D", "hydrologyBanks3D"})
|
||||
@EqualsAndHashCode(exclude = {"data", "gridBoundsCache", "sharedCornerBounds", "terrainProgram", "bakeCache", "sparseBaseHeights", "biomeRegistry", "regionRegistry", "blockRegistry", "regionSource", "baseBiomeSource", "caveSource", "rockSource", "fluidSource", "childStyleSeed", "inferredByType", "naturalHeightSource", "naturalTrueBiomeSource", "trueBiomeSource", "frozenInterpolators", "frozenGenerators", "inferredBiomeStreams", "hydrologyRuntime", "imageMapRuntime", "terrainEngine", "terrain3D", "hydrologyBanks3D"})
|
||||
@ToString(exclude = {"data", "gridBoundsCache", "sharedCornerBounds", "terrainProgram", "bakeCache", "sparseBaseHeights", "biomeRegistry", "regionRegistry", "blockRegistry", "regionSource", "baseBiomeSource", "caveSource", "rockSource", "fluidSource", "childStyleSeed", "inferredByType", "naturalHeightSource", "naturalTrueBiomeSource", "trueBiomeSource", "frozenInterpolators", "frozenGenerators", "inferredBiomeStreams", "hydrologyRuntime", "imageMapRuntime", "terrainEngine", "terrain3D", "hydrologyBanks3D"})
|
||||
public class IrisComplex implements DataProvider {
|
||||
private static final NoiseBounds ZERO_NOISE_BOUNDS = new NoiseBounds(0D, 0D);
|
||||
private static final AtomicLong lastBoundsFailureLog = new AtomicLong(0L);
|
||||
@@ -182,6 +183,10 @@ public class IrisComplex implements DataProvider {
|
||||
@Getter(AccessLevel.NONE)
|
||||
private final transient BakeRegistry<NativeBlockState> blockRegistry = new BakeRegistry<>();
|
||||
@Getter(AccessLevel.NONE)
|
||||
private final transient long childStyleSeed;
|
||||
@Getter(AccessLevel.NONE)
|
||||
private final transient Map<InferredType, ProceduralStream<IrisBiome>> inferredByType;
|
||||
@Getter(AccessLevel.NONE)
|
||||
private final transient ProceduralStream<IrisRegion> regionSource;
|
||||
@Getter(AccessLevel.NONE)
|
||||
private final transient ProceduralStream<IrisBiome> baseBiomeSource;
|
||||
@@ -321,6 +326,7 @@ public class IrisComplex implements DataProvider {
|
||||
IrisBiome emptyBiome = new IrisBiome().setInferredType(InferredType.CAVE);
|
||||
UUID focusUUID = UUID.nameUUIDFromBytes("focus".getBytes());
|
||||
this.rng = new RNG(engine.getSeedManager().getComplex());
|
||||
childStyleSeed = IrisBiome.childStyleSeed(rng, 123);
|
||||
this.data = engine.getData();
|
||||
imageMapRuntime = IrisImageMapRuntime.compile(engine);
|
||||
double height = engine.getMaxHeight();
|
||||
@@ -334,7 +340,8 @@ public class IrisComplex implements DataProvider {
|
||||
boolean buffetActive = biomeBuffet != null && !biomeBuffet.cells().isEmpty();
|
||||
focusBiome = buffetActive ? null : compatUsable(engine.getFocus());
|
||||
focusRegion = buffetActive ? null : compatUsable(engine.getFocusRegion());
|
||||
Map<InferredType, ProceduralStream<IrisBiome>> inferredStreams = new HashMap<>();
|
||||
Map<InferredType, ProceduralStream<IrisBiome>> inferredStreams = new EnumMap<>(InferredType.class);
|
||||
inferredByType = inferredStreams;
|
||||
KList<IrisRegion> preparedRegions = new KList<>();
|
||||
|
||||
if (focusBiome != null) {
|
||||
@@ -442,9 +449,10 @@ public class IrisComplex implements DataProvider {
|
||||
bridgeStream = focusedBiomes != null ? focusedBiomes.convert(IrisBiome::getInferredType) :
|
||||
ContinentGenerator.create(engine.getDimension(), rng, getData()).stream()
|
||||
.convert((v) -> v >= engine.getDimension().getLandChance() ? InferredType.SEA : InferredType.LAND);
|
||||
ProceduralStream<IrisBiome> proceduralBaseBiomeStream = focusedBiomes != null ? focusedBiomes :
|
||||
bridgeStream.convertAware2D((t, x, z) -> inferredStreams.get(t).get(x, z))
|
||||
.convertAware2D(this::implode);
|
||||
ProceduralStream<IrisBiome> proceduralBaseBiomeStream = focusedBiomes != null ? focusedBiomes
|
||||
: ProceduralStream.of(this::proceduralBaseBiome,
|
||||
(x, y, z) -> implode(inferredByType.get(bridgeStream.get(x, y, z)).get(x, z), x, z),
|
||||
Interpolated.of(value -> 0D, value -> null));
|
||||
baseBiomeSource = focusedBiomes != null || !imageMapRuntime.has(IrisImageMapApplication.BIOME)
|
||||
? proceduralBaseBiomeStream : ProceduralStream.of((x, z) -> {
|
||||
IrisBiome mapped = imageMapRuntime.sampleBiome(x, z);
|
||||
@@ -1103,6 +1111,10 @@ public class IrisComplex implements DataProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private IrisBiome proceduralBaseBiome(double x, double z) {
|
||||
return implode(inferredByType.get(bridgeStream.get(x, z)).get(x, z), x, z);
|
||||
}
|
||||
|
||||
private boolean bakedColumn(BakePlane plane, int blockX, int blockZ) {
|
||||
ChunkPlanes.Plane values = bakeCache.planeIfPresent(plane, blockX >> 4, blockZ >> 4);
|
||||
return values != null && values.ready(((blockZ & 15) << 4) | (blockX & 15));
|
||||
@@ -1367,29 +1379,29 @@ public class IrisComplex implements DataProvider {
|
||||
IrisBiome emptyBiome
|
||||
) {
|
||||
return switch (inferredType) {
|
||||
case CAVE -> engine.getDimension().getCaveBiomeStyle()
|
||||
case CAVE -> new RarityStream<>(engine.getDimension().getCaveBiomeStyle()
|
||||
.create(rng.nextParallelRNG(InferredType.CAVE.ordinal()), getData()).stream()
|
||||
.zoom(engine.getDimension().getBiomeZoom())
|
||||
.zoom(region.getCaveBiomeZoom())
|
||||
.selectRarity(loadInferredBiomes(region.getCaveBiomes(), InferredType.CAVE))
|
||||
.zoom(region.getCaveBiomeZoom()),
|
||||
loadInferredBiomes(region.getCaveBiomes(), InferredType.CAVE))
|
||||
.onNull(emptyBiome);
|
||||
case LAND -> engine.getDimension().getLandBiomeStyle()
|
||||
case LAND -> new RarityStream<>(engine.getDimension().getLandBiomeStyle()
|
||||
.create(rng.nextParallelRNG(InferredType.LAND.ordinal()), getData()).stream()
|
||||
.zoom(engine.getDimension().getBiomeZoom())
|
||||
.zoom(engine.getDimension().getLandZoom())
|
||||
.zoom(region.getLandBiomeZoom())
|
||||
.selectRarity(loadInferredBiomes(region.getLandBiomes(), InferredType.LAND));
|
||||
case SEA -> engine.getDimension().getSeaBiomeStyle()
|
||||
.zoom(region.getLandBiomeZoom()),
|
||||
loadInferredBiomes(region.getLandBiomes(), InferredType.LAND));
|
||||
case SEA -> new RarityStream<>(engine.getDimension().getSeaBiomeStyle()
|
||||
.create(rng.nextParallelRNG(InferredType.SEA.ordinal()), getData()).stream()
|
||||
.zoom(engine.getDimension().getBiomeZoom())
|
||||
.zoom(engine.getDimension().getSeaZoom())
|
||||
.zoom(region.getSeaBiomeZoom())
|
||||
.selectRarity(loadInferredBiomes(region.getSeaBiomes(), InferredType.SEA));
|
||||
case SHORE -> engine.getDimension().getShoreBiomeStyle()
|
||||
.zoom(region.getSeaBiomeZoom()),
|
||||
loadInferredBiomes(region.getSeaBiomes(), InferredType.SEA));
|
||||
case SHORE -> new RarityStream<>(engine.getDimension().getShoreBiomeStyle()
|
||||
.create(rng.nextParallelRNG(InferredType.SHORE.ordinal()), getData()).stream()
|
||||
.zoom(engine.getDimension().getBiomeZoom())
|
||||
.zoom(region.getShoreBiomeZoom())
|
||||
.selectRarity(loadInferredBiomes(region.getShoreBiomes(), InferredType.SHORE));
|
||||
.zoom(region.getShoreBiomeZoom()),
|
||||
loadInferredBiomes(region.getShoreBiomes(), InferredType.SHORE));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2020,7 +2032,7 @@ public class IrisComplex implements DataProvider {
|
||||
return biomeKey == null ? sample.parentBiomeKey() : biomeKey;
|
||||
}
|
||||
|
||||
private IrisBiome fixBiomeType(Double height, IrisBiome biome, IrisRegion region, Double x, Double z, double fluidHeight) {
|
||||
private IrisBiome fixBiomeType(double height, IrisBiome biome, IrisRegion region, double x, double z, double fluidHeight) {
|
||||
IrisBiome resolved = resolveSurfaceBiome(
|
||||
height,
|
||||
biome,
|
||||
@@ -2633,7 +2645,7 @@ public class IrisComplex implements DataProvider {
|
||||
return computed;
|
||||
}
|
||||
|
||||
private IrisBiome implode(IrisBiome b, Double x, Double z) {
|
||||
private IrisBiome implode(IrisBiome b, double x, double z) {
|
||||
if (b.getChildren().isEmpty()) {
|
||||
return b;
|
||||
}
|
||||
@@ -2641,7 +2653,7 @@ public class IrisComplex implements DataProvider {
|
||||
return implode(b, x, z, 3);
|
||||
}
|
||||
|
||||
private IrisBiome implode(IrisBiome b, Double x, Double z, int max) {
|
||||
private IrisBiome implode(IrisBiome b, double x, double z, int max) {
|
||||
if (max < 0) {
|
||||
return b;
|
||||
}
|
||||
@@ -2650,7 +2662,7 @@ public class IrisComplex implements DataProvider {
|
||||
return b;
|
||||
}
|
||||
|
||||
CNG childCell = b.getChildrenGenerator(rng, 123, b.getChildShrinkFactor());
|
||||
CNG childCell = b.getChildrenGenerator(childStyleSeed, b.getChildShrinkFactor());
|
||||
ChildSelectionPlan childSelectionPlan = resolveChildSelectionPlan(b);
|
||||
IrisBiome biome = childSelectionPlan.select(childCell, x, z).withInferredType(b.getInferredType());
|
||||
if (biome == b && hasFixedNoise(b.getChildStyle())) {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package art.arcane.iris.generation.stream;
|
||||
|
||||
import art.arcane.volmlib.util.math.Rarity;
|
||||
import art.arcane.volmlib.util.stream.BasicStream;
|
||||
import art.arcane.volmlib.util.stream.ProceduralStream;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class RarityStream<T extends Rarity> extends BasicStream<T> {
|
||||
private final ProceduralStream<?> noise;
|
||||
private final Object[] options;
|
||||
private final double[] cumulative;
|
||||
|
||||
public RarityStream(ProceduralStream<?> noise, List<T> options) {
|
||||
super();
|
||||
this.noise = Objects.requireNonNull(noise, "Rarity noise");
|
||||
this.options = options.toArray();
|
||||
this.cumulative = new double[this.options.length];
|
||||
double buffer = 0;
|
||||
for (int index = 0; index < this.options.length; index++) {
|
||||
buffer += 1d / Rarity.get(this.options[index]);
|
||||
cumulative[index] = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(double x, double z) {
|
||||
return pick(noise.getDouble(x, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(double x, double y, double z) {
|
||||
return pick(noise.getDouble(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double toDouble(T t) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T fromDouble(double d) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T pick(double noiseValue) {
|
||||
int count = options.length;
|
||||
if (count == 0) {
|
||||
return null;
|
||||
}
|
||||
if (count == 1) {
|
||||
return (T) options[0];
|
||||
}
|
||||
double threshold = cumulative[count - 1] * noiseValue;
|
||||
for (int index = 0; index < count; index++) {
|
||||
if (cumulative[index] >= threshold) {
|
||||
return (T) options[index];
|
||||
}
|
||||
}
|
||||
return (T) options[count - 1];
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package art.arcane.iris.generation.terrain;
|
||||
import art.arcane.iris.generation.biome.IrisBiome;
|
||||
import art.arcane.iris.generation.image.IrisImageMapRuntime;
|
||||
import art.arcane.iris.generation.image.IrisImageMapApplication;
|
||||
import art.arcane.iris.generation.stream.RarityStream;
|
||||
import art.arcane.iris.pack.loading.IrisData;
|
||||
import art.arcane.iris.studio.generation.BiomeBuffetLayout;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
@@ -24,7 +25,7 @@ public final class RegionSelection {
|
||||
regions.add(region);
|
||||
}
|
||||
}
|
||||
ProceduralStream<IrisRegion> selected = options.style().selectRarity(regions);
|
||||
ProceduralStream<IrisRegion> selected = new RarityStream<>(options.style(), regions);
|
||||
if (!options.imageMaps().has(IrisImageMapApplication.REGION)) {
|
||||
return selected;
|
||||
}
|
||||
|
||||
+2
-2
@@ -83,7 +83,7 @@ public class IrisComplexSelfSelectionTest {
|
||||
Field field = IrisComplex.class.getDeclaredField("childSelectionPlans");
|
||||
field.setAccessible(true);
|
||||
field.set(complex, plans);
|
||||
implode = IrisComplex.class.getDeclaredMethod("implode", IrisBiome.class, Double.class, Double.class, int.class);
|
||||
implode = IrisComplex.class.getDeclaredMethod("implode", IrisBiome.class, double.class, double.class, int.class);
|
||||
implode.setAccessible(true);
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ public class IrisComplexSelfSelectionTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CNG getChildrenGenerator(RNG rng, int signature, double scale) {
|
||||
public CNG getChildrenGenerator(long seed, double scale) {
|
||||
return noise;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -97,11 +97,11 @@ public class IrisCarveModifierInferenceIsolationTest {
|
||||
doReturn(0D).when(region).getShoreHeight(anyDouble(), anyDouble());
|
||||
Method fixBiomeType = IrisComplex.class.getDeclaredMethod(
|
||||
"fixBiomeType",
|
||||
Double.class,
|
||||
double.class,
|
||||
IrisBiome.class,
|
||||
IrisRegion.class,
|
||||
Double.class,
|
||||
Double.class,
|
||||
double.class,
|
||||
double.class,
|
||||
double.class
|
||||
);
|
||||
fixBiomeType.setAccessible(true);
|
||||
|
||||
Reference in new issue
Block a user