/* * Decompiled with CFR 0.152. * * Could not load the following classes: * com.mojang.datafixers.kinds.App * com.mojang.datafixers.kinds.Applicative * com.mojang.datafixers.util.Either * com.mojang.logging.LogUtils * com.mojang.serialization.Codec * com.mojang.serialization.MapCodec * com.mojang.serialization.codecs.RecordCodecBuilder * it.unimi.dsi.fastutil.doubles.Double2DoubleFunction * org.slf4j.Logger */ package net.minecraft.world.level.levelgen; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.Applicative; import com.mojang.datafixers.util.Either; import com.mojang.logging.LogUtils; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; import java.util.Arrays; import java.util.Optional; import java.util.function.BiFunction; import java.util.function.Function; import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceKey; import net.minecraft.util.BoundedFloatFunction; import net.minecraft.util.CubicSpline; import net.minecraft.util.ExtraCodecs; import net.minecraft.util.KeyDispatchDataCodec; import net.minecraft.util.Mth; import net.minecraft.util.StringRepresentable; import net.minecraft.util.VisibleForDebug; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.levelgen.DensityFunction; import net.minecraft.world.level.levelgen.LegacyRandomSource; import net.minecraft.world.level.levelgen.NoiseRouterData; import net.minecraft.world.level.levelgen.synth.BlendedNoise; import net.minecraft.world.level.levelgen.synth.NormalNoise; import net.minecraft.world.level.levelgen.synth.SimplexNoise; import org.slf4j.Logger; public final class DensityFunctions { private static final Codec CODEC = BuiltInRegistries.DENSITY_FUNCTION_TYPE.byNameCodec().dispatch(function -> function.codec().codec(), Function.identity()); protected static final double MAX_REASONABLE_NOISE_VALUE = 1000000.0; private static final Codec NOISE_VALUE_CODEC = Codec.doubleRange((double)-1000000.0, (double)1000000.0); public static final Codec DIRECT_CODEC = Codec.either(NOISE_VALUE_CODEC, CODEC).xmap(either -> (DensityFunction)either.map(DensityFunctions::constant, Function.identity()), function -> { if (function instanceof Constant) { Constant constant = (Constant)function; return Either.left((Object)constant.value()); } return Either.right((Object)function); }); public static MapCodec bootstrap(Registry> registry) { DensityFunctions.register(registry, "blend_alpha", BlendAlpha.CODEC); DensityFunctions.register(registry, "blend_offset", BlendOffset.CODEC); DensityFunctions.register(registry, "beardifier", BeardifierMarker.CODEC); DensityFunctions.register(registry, "old_blended_noise", BlendedNoise.CODEC); for (Marker.Type type : Marker.Type.values()) { DensityFunctions.register(registry, type.getSerializedName(), type.codec); } DensityFunctions.register(registry, "noise", Noise.CODEC); DensityFunctions.register(registry, "end_islands", EndIslandDensityFunction.CODEC); DensityFunctions.register(registry, "weird_scaled_sampler", WeirdScaledSampler.CODEC); DensityFunctions.register(registry, "shifted_noise", ShiftedNoise.CODEC); DensityFunctions.register(registry, "range_choice", RangeChoice.CODEC); DensityFunctions.register(registry, "shift_a", ShiftA.CODEC); DensityFunctions.register(registry, "shift_b", ShiftB.CODEC); DensityFunctions.register(registry, "shift", Shift.CODEC); DensityFunctions.register(registry, "blend_density", BlendDensity.CODEC); DensityFunctions.register(registry, "clamp", Clamp.CODEC); for (Enum enum_ : Mapped.Type.values()) { DensityFunctions.register(registry, ((Mapped.Type)enum_).getSerializedName(), ((Mapped.Type)enum_).codec); } for (Enum enum_ : TwoArgumentSimpleFunction.Type.values()) { DensityFunctions.register(registry, ((TwoArgumentSimpleFunction.Type)enum_).getSerializedName(), ((TwoArgumentSimpleFunction.Type)enum_).codec); } DensityFunctions.register(registry, "spline", Spline.CODEC); DensityFunctions.register(registry, "constant", Constant.CODEC); DensityFunctions.register(registry, "y_clamped_gradient", YClampedGradient.CODEC); return DensityFunctions.register(registry, "find_top_surface", FindTopSurface.CODEC); } private static MapCodec register(Registry> registry, String name, KeyDispatchDataCodec codec) { return Registry.register(registry, name, codec.codec()); } private static KeyDispatchDataCodec singleArgumentCodec(Codec argumentCodec, Function constructor, Function getter) { return KeyDispatchDataCodec.of(argumentCodec.fieldOf("argument").xmap(constructor, getter)); } private static KeyDispatchDataCodec singleFunctionArgumentCodec(Function constructor, Function getter) { return DensityFunctions.singleArgumentCodec(DensityFunction.HOLDER_HELPER_CODEC, constructor, getter); } private static KeyDispatchDataCodec doubleFunctionArgumentCodec(BiFunction constructor, Function firstArgumentGetter, Function secondArgumentGetter) { return KeyDispatchDataCodec.of(RecordCodecBuilder.mapCodec(i -> i.group((App)DensityFunction.HOLDER_HELPER_CODEC.fieldOf("argument1").forGetter(firstArgumentGetter), (App)DensityFunction.HOLDER_HELPER_CODEC.fieldOf("argument2").forGetter(secondArgumentGetter)).apply((Applicative)i, constructor))); } private static KeyDispatchDataCodec makeCodec(MapCodec dataCodec) { return KeyDispatchDataCodec.of(dataCodec); } private DensityFunctions() { } public static DensityFunction interpolated(DensityFunction function) { return new Marker(Marker.Type.Interpolated, function); } public static DensityFunction flatCache(DensityFunction function) { return new Marker(Marker.Type.FlatCache, function); } public static DensityFunction cache2d(DensityFunction function) { return new Marker(Marker.Type.Cache2D, function); } public static DensityFunction cacheOnce(DensityFunction function) { return new Marker(Marker.Type.CacheOnce, function); } public static DensityFunction cacheAllInCell(DensityFunction function) { return new Marker(Marker.Type.CacheAllInCell, function); } public static DensityFunction mappedNoise(Holder noiseData, @Deprecated double xzScale, double yScale, double minTarget, double maxTarget) { return DensityFunctions.mapFromUnitTo(new Noise(new DensityFunction.NoiseHolder(noiseData), xzScale, yScale), minTarget, maxTarget); } public static DensityFunction mappedNoise(Holder noiseData, double yScale, double minTarget, double maxTarget) { return DensityFunctions.mappedNoise(noiseData, 1.0, yScale, minTarget, maxTarget); } public static DensityFunction mappedNoise(Holder noiseData, double minTarget, double maxTarget) { return DensityFunctions.mappedNoise(noiseData, 1.0, 1.0, minTarget, maxTarget); } public static DensityFunction shiftedNoise2d(DensityFunction shiftX, DensityFunction shiftZ, double xzScale, Holder noiseData) { return new ShiftedNoise(shiftX, DensityFunctions.zero(), shiftZ, xzScale, 0.0, new DensityFunction.NoiseHolder(noiseData)); } public static DensityFunction noise(Holder noiseData) { return DensityFunctions.noise(noiseData, 1.0, 1.0); } public static DensityFunction noise(Holder noiseData, double xzScale, double yScale) { return new Noise(new DensityFunction.NoiseHolder(noiseData), xzScale, yScale); } public static DensityFunction noise(Holder noiseData, double yScale) { return DensityFunctions.noise(noiseData, 1.0, yScale); } public static DensityFunction rangeChoice(DensityFunction input, double minInclusive, double maxExclusive, DensityFunction whenInRange, DensityFunction whenOutOfRange) { return new RangeChoice(input, minInclusive, maxExclusive, whenInRange, whenOutOfRange); } public static DensityFunction shiftA(Holder noiseData) { return new ShiftA(new DensityFunction.NoiseHolder(noiseData)); } public static DensityFunction shiftB(Holder noiseData) { return new ShiftB(new DensityFunction.NoiseHolder(noiseData)); } public static DensityFunction shift(Holder noiseData) { return new Shift(new DensityFunction.NoiseHolder(noiseData)); } public static DensityFunction blendDensity(DensityFunction input) { return new BlendDensity(input); } public static DensityFunction endIslands(long seed) { return new EndIslandDensityFunction(seed); } public static DensityFunction weirdScaledSampler(DensityFunction input, Holder noiseData, WeirdScaledSampler.RarityValueMapper rarityValueMapper) { return new WeirdScaledSampler(input, new DensityFunction.NoiseHolder(noiseData), rarityValueMapper); } public static DensityFunction add(DensityFunction f1, DensityFunction f2) { return TwoArgumentSimpleFunction.create(TwoArgumentSimpleFunction.Type.ADD, f1, f2); } public static DensityFunction mul(DensityFunction f1, DensityFunction f2) { return TwoArgumentSimpleFunction.create(TwoArgumentSimpleFunction.Type.MUL, f1, f2); } public static DensityFunction min(DensityFunction f1, DensityFunction f2) { return TwoArgumentSimpleFunction.create(TwoArgumentSimpleFunction.Type.MIN, f1, f2); } public static DensityFunction max(DensityFunction f1, DensityFunction f2) { return TwoArgumentSimpleFunction.create(TwoArgumentSimpleFunction.Type.MAX, f1, f2); } public static DensityFunction spline(CubicSpline spline) { return new Spline(spline); } public static DensityFunction zero() { return Constant.ZERO; } public static DensityFunction constant(double value) { return new Constant(value); } public static DensityFunction yClampedGradient(int fromY, int toY, double fromValue, double toValue) { return new YClampedGradient(fromY, toY, fromValue, toValue); } public static DensityFunction map(DensityFunction function, Mapped.Type type) { return Mapped.create(type, function); } private static DensityFunction mapFromUnitTo(DensityFunction function, double min, double max) { double middle = (min + max) * 0.5; double factor = (max - min) * 0.5; return DensityFunctions.add(DensityFunctions.constant(middle), DensityFunctions.mul(DensityFunctions.constant(factor), function)); } public static DensityFunction blendAlpha() { return BlendAlpha.INSTANCE; } public static DensityFunction blendOffset() { return BlendOffset.INSTANCE; } public static DensityFunction lerp(DensityFunction alpha, DensityFunction first, DensityFunction second) { if (first instanceof Constant) { Constant constant = (Constant)first; return DensityFunctions.lerp(alpha, constant.value, second); } DensityFunction alphaCached = DensityFunctions.cacheOnce(alpha); DensityFunction oneMinusAlpha = DensityFunctions.add(DensityFunctions.mul(alphaCached, DensityFunctions.constant(-1.0)), DensityFunctions.constant(1.0)); return DensityFunctions.add(DensityFunctions.mul(first, oneMinusAlpha), DensityFunctions.mul(second, alphaCached)); } public static DensityFunction lerp(DensityFunction factor, double first, DensityFunction second) { return DensityFunctions.add(DensityFunctions.mul(factor, DensityFunctions.add(second, DensityFunctions.constant(-first))), DensityFunctions.constant(first)); } public static DensityFunction findTopSurface(DensityFunction density, DensityFunction upperBound, int lowerBound, int stepSize) { return new FindTopSurface(density, upperBound, lowerBound, stepSize); } protected static enum BlendAlpha implements DensityFunction.SimpleFunction { INSTANCE; public static final KeyDispatchDataCodec CODEC; @Override public double compute(DensityFunction.FunctionContext context) { return 1.0; } @Override public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) { Arrays.fill(output, 1.0); } @Override public double minValue() { return 1.0; } @Override public double maxValue() { return 1.0; } @Override public KeyDispatchDataCodec codec() { return CODEC; } static { CODEC = KeyDispatchDataCodec.of(MapCodec.unit((Object)INSTANCE)); } } protected static enum BlendOffset implements DensityFunction.SimpleFunction { INSTANCE; public static final KeyDispatchDataCodec CODEC; @Override public double compute(DensityFunction.FunctionContext context) { return 0.0; } @Override public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) { Arrays.fill(output, 0.0); } @Override public double minValue() { return 0.0; } @Override public double maxValue() { return 0.0; } @Override public KeyDispatchDataCodec codec() { return CODEC; } static { CODEC = KeyDispatchDataCodec.of(MapCodec.unit((Object)INSTANCE)); } } protected static enum BeardifierMarker implements BeardifierOrMarker { INSTANCE; @Override public double compute(DensityFunction.FunctionContext context) { return 0.0; } @Override public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) { Arrays.fill(output, 0.0); } @Override public double minValue() { return 0.0; } @Override public double maxValue() { return 0.0; } } protected record Marker(Type type, DensityFunction wrapped) implements MarkerOrMarked { @Override public double compute(DensityFunction.FunctionContext context) { return this.wrapped.compute(context); } @Override public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) { this.wrapped.fillArray(output, contextProvider); } @Override public double minValue() { return this.wrapped.minValue(); } @Override public double maxValue() { return this.wrapped.maxValue(); } static enum Type implements StringRepresentable { Interpolated("interpolated"), FlatCache("flat_cache"), Cache2D("cache_2d"), CacheOnce("cache_once"), CacheAllInCell("cache_all_in_cell"); private final String name; private final KeyDispatchDataCodec codec = DensityFunctions.singleFunctionArgumentCodec(input -> new Marker(this, (DensityFunction)input), MarkerOrMarked::wrapped); private Type(String name) { this.name = name; } @Override public String getSerializedName() { return this.name; } } } protected record Noise(DensityFunction.NoiseHolder noise, @Deprecated double xzScale, double yScale) implements DensityFunction { public static final MapCodec DATA_CODEC = RecordCodecBuilder.mapCodec(i -> i.group((App)DensityFunction.NoiseHolder.CODEC.fieldOf("noise").forGetter(Noise::noise), (App)Codec.DOUBLE.fieldOf("xz_scale").forGetter(Noise::xzScale), (App)Codec.DOUBLE.fieldOf("y_scale").forGetter(Noise::yScale)).apply((Applicative)i, Noise::new)); public static final KeyDispatchDataCodec CODEC = DensityFunctions.makeCodec(DATA_CODEC); @Override public double compute(DensityFunction.FunctionContext context) { return this.noise.getValue((double)context.blockX() * this.xzScale, (double)context.blockY() * this.yScale, (double)context.blockZ() * this.xzScale); } @Override public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) { contextProvider.fillAllDirectly(output, this); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new Noise(visitor.visitNoise(this.noise), this.xzScale, this.yScale)); } @Override public double minValue() { return -this.maxValue(); } @Override public double maxValue() { return this.noise.maxValue(); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } protected static final class EndIslandDensityFunction implements DensityFunction.SimpleFunction { public static final KeyDispatchDataCodec CODEC = KeyDispatchDataCodec.of(MapCodec.unit((Object)new EndIslandDensityFunction(0L))); private static final float ISLAND_THRESHOLD = -0.9f; private final SimplexNoise islandNoise; public EndIslandDensityFunction(long seed) { LegacyRandomSource islandRandom = new LegacyRandomSource(seed); islandRandom.consumeCount(17292); this.islandNoise = new SimplexNoise(islandRandom); } private static float getHeightValue(SimplexNoise islandNoise, int sectionX, int sectionZ) { int chunkX = sectionX / 2; int chunkZ = sectionZ / 2; int subSectionX = sectionX % 2; int subSectionZ = sectionZ % 2; float doffs = 100.0f - Mth.sqrt(sectionX * sectionX + sectionZ * sectionZ) * 8.0f; doffs = Mth.clamp(doffs, -100.0f, 80.0f); for (int xo = -12; xo <= 12; ++xo) { for (int zo = -12; zo <= 12; ++zo) { long totalChunkX = chunkX + xo; long totalChunkZ = chunkZ + zo; if (totalChunkX * totalChunkX + totalChunkZ * totalChunkZ <= 4096L || !(islandNoise.getValue(totalChunkX, totalChunkZ) < (double)-0.9f)) continue; float islandSize = (Mth.abs(totalChunkX) * 3439.0f + Mth.abs(totalChunkZ) * 147.0f) % 13.0f + 9.0f; float xd = subSectionX - xo * 2; float zd = subSectionZ - zo * 2; float newDoffs = 100.0f - Mth.sqrt(xd * xd + zd * zd) * islandSize; newDoffs = Mth.clamp(newDoffs, -100.0f, 80.0f); doffs = Math.max(doffs, newDoffs); } } return doffs; } @Override public double compute(DensityFunction.FunctionContext context) { return ((double)EndIslandDensityFunction.getHeightValue(this.islandNoise, context.blockX() / 8, context.blockZ() / 8) - 8.0) / 128.0; } @Override public double minValue() { return -0.84375; } @Override public double maxValue() { return 0.5625; } @Override public KeyDispatchDataCodec codec() { return CODEC; } } protected record WeirdScaledSampler(DensityFunction input, DensityFunction.NoiseHolder noise, RarityValueMapper rarityValueMapper) implements TransformerWithContext { private static final MapCodec DATA_CODEC = RecordCodecBuilder.mapCodec(i -> i.group((App)DensityFunction.HOLDER_HELPER_CODEC.fieldOf("input").forGetter(WeirdScaledSampler::input), (App)DensityFunction.NoiseHolder.CODEC.fieldOf("noise").forGetter(WeirdScaledSampler::noise), (App)RarityValueMapper.CODEC.fieldOf("rarity_value_mapper").forGetter(WeirdScaledSampler::rarityValueMapper)).apply((Applicative)i, WeirdScaledSampler::new)); public static final KeyDispatchDataCodec CODEC = DensityFunctions.makeCodec(DATA_CODEC); @Override public double transform(DensityFunction.FunctionContext context, double input) { double rarity = this.rarityValueMapper.mapper.get(input); return rarity * Math.abs(this.noise.getValue((double)context.blockX() / rarity, (double)context.blockY() / rarity, (double)context.blockZ() / rarity)); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new WeirdScaledSampler(this.input.mapAll(visitor), visitor.visitNoise(this.noise), this.rarityValueMapper)); } @Override public double minValue() { return 0.0; } @Override public double maxValue() { return this.rarityValueMapper.maxRarity * this.noise.maxValue(); } @Override public KeyDispatchDataCodec codec() { return CODEC; } public static enum RarityValueMapper implements StringRepresentable { TYPE1("type_1", NoiseRouterData.QuantizedSpaghettiRarity::getSpaghettiRarity3D, 2.0), TYPE2("type_2", NoiseRouterData.QuantizedSpaghettiRarity::getSphaghettiRarity2D, 3.0); public static final Codec CODEC; private final String name; private final Double2DoubleFunction mapper; private final double maxRarity; private RarityValueMapper(String name, Double2DoubleFunction mapper, double maxRarity) { this.name = name; this.mapper = mapper; this.maxRarity = maxRarity; } @Override public String getSerializedName() { return this.name; } static { CODEC = StringRepresentable.fromEnum(RarityValueMapper::values); } } } protected record ShiftedNoise(DensityFunction shiftX, DensityFunction shiftY, DensityFunction shiftZ, double xzScale, double yScale, DensityFunction.NoiseHolder noise) implements DensityFunction { private static final MapCodec DATA_CODEC = RecordCodecBuilder.mapCodec(i -> i.group((App)DensityFunction.HOLDER_HELPER_CODEC.fieldOf("shift_x").forGetter(ShiftedNoise::shiftX), (App)DensityFunction.HOLDER_HELPER_CODEC.fieldOf("shift_y").forGetter(ShiftedNoise::shiftY), (App)DensityFunction.HOLDER_HELPER_CODEC.fieldOf("shift_z").forGetter(ShiftedNoise::shiftZ), (App)Codec.DOUBLE.fieldOf("xz_scale").forGetter(ShiftedNoise::xzScale), (App)Codec.DOUBLE.fieldOf("y_scale").forGetter(ShiftedNoise::yScale), (App)DensityFunction.NoiseHolder.CODEC.fieldOf("noise").forGetter(ShiftedNoise::noise)).apply((Applicative)i, ShiftedNoise::new)); public static final KeyDispatchDataCodec CODEC = DensityFunctions.makeCodec(DATA_CODEC); @Override public double compute(DensityFunction.FunctionContext context) { double x = (double)context.blockX() * this.xzScale + this.shiftX.compute(context); double y = (double)context.blockY() * this.yScale + this.shiftY.compute(context); double z = (double)context.blockZ() * this.xzScale + this.shiftZ.compute(context); return this.noise.getValue(x, y, z); } @Override public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) { contextProvider.fillAllDirectly(output, this); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new ShiftedNoise(this.shiftX.mapAll(visitor), this.shiftY.mapAll(visitor), this.shiftZ.mapAll(visitor), this.xzScale, this.yScale, visitor.visitNoise(this.noise))); } @Override public double minValue() { return -this.maxValue(); } @Override public double maxValue() { return this.noise.maxValue(); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } private record RangeChoice(DensityFunction input, double minInclusive, double maxExclusive, DensityFunction whenInRange, DensityFunction whenOutOfRange) implements DensityFunction { public static final MapCodec DATA_CODEC = RecordCodecBuilder.mapCodec(i -> i.group((App)DensityFunction.HOLDER_HELPER_CODEC.fieldOf("input").forGetter(RangeChoice::input), (App)NOISE_VALUE_CODEC.fieldOf("min_inclusive").forGetter(RangeChoice::minInclusive), (App)NOISE_VALUE_CODEC.fieldOf("max_exclusive").forGetter(RangeChoice::maxExclusive), (App)DensityFunction.HOLDER_HELPER_CODEC.fieldOf("when_in_range").forGetter(RangeChoice::whenInRange), (App)DensityFunction.HOLDER_HELPER_CODEC.fieldOf("when_out_of_range").forGetter(RangeChoice::whenOutOfRange)).apply((Applicative)i, RangeChoice::new)); public static final KeyDispatchDataCodec CODEC = DensityFunctions.makeCodec(DATA_CODEC); @Override public double compute(DensityFunction.FunctionContext context) { double inputValue = this.input.compute(context); if (inputValue >= this.minInclusive && inputValue < this.maxExclusive) { return this.whenInRange.compute(context); } return this.whenOutOfRange.compute(context); } @Override public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) { this.input.fillArray(output, contextProvider); for (int i = 0; i < output.length; ++i) { double v = output[i]; output[i] = v >= this.minInclusive && v < this.maxExclusive ? this.whenInRange.compute(contextProvider.forIndex(i)) : this.whenOutOfRange.compute(contextProvider.forIndex(i)); } } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new RangeChoice(this.input.mapAll(visitor), this.minInclusive, this.maxExclusive, this.whenInRange.mapAll(visitor), this.whenOutOfRange.mapAll(visitor))); } @Override public double minValue() { return Math.min(this.whenInRange.minValue(), this.whenOutOfRange.minValue()); } @Override public double maxValue() { return Math.max(this.whenInRange.maxValue(), this.whenOutOfRange.maxValue()); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } protected record ShiftA(DensityFunction.NoiseHolder offsetNoise) implements ShiftNoise { private static final KeyDispatchDataCodec CODEC = DensityFunctions.singleArgumentCodec(DensityFunction.NoiseHolder.CODEC, ShiftA::new, ShiftA::offsetNoise); @Override public double compute(DensityFunction.FunctionContext context) { return this.compute(context.blockX(), 0.0, context.blockZ()); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new ShiftA(visitor.visitNoise(this.offsetNoise))); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } protected record ShiftB(DensityFunction.NoiseHolder offsetNoise) implements ShiftNoise { private static final KeyDispatchDataCodec CODEC = DensityFunctions.singleArgumentCodec(DensityFunction.NoiseHolder.CODEC, ShiftB::new, ShiftB::offsetNoise); @Override public double compute(DensityFunction.FunctionContext context) { return this.compute(context.blockZ(), context.blockX(), 0.0); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new ShiftB(visitor.visitNoise(this.offsetNoise))); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } protected record Shift(DensityFunction.NoiseHolder offsetNoise) implements ShiftNoise { private static final KeyDispatchDataCodec CODEC = DensityFunctions.singleArgumentCodec(DensityFunction.NoiseHolder.CODEC, Shift::new, Shift::offsetNoise); @Override public double compute(DensityFunction.FunctionContext context) { return this.compute(context.blockX(), context.blockY(), context.blockZ()); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new Shift(visitor.visitNoise(this.offsetNoise))); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } private record BlendDensity(DensityFunction input) implements TransformerWithContext { private static final KeyDispatchDataCodec CODEC = DensityFunctions.singleFunctionArgumentCodec(BlendDensity::new, BlendDensity::input); @Override public double transform(DensityFunction.FunctionContext context, double input) { return context.getBlender().blendDensity(context, input); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new BlendDensity(this.input.mapAll(visitor))); } @Override public double minValue() { return Double.NEGATIVE_INFINITY; } @Override public double maxValue() { return Double.POSITIVE_INFINITY; } @Override public KeyDispatchDataCodec codec() { return CODEC; } } protected record Clamp(DensityFunction input, double minValue, double maxValue) implements PureTransformer { private static final MapCodec DATA_CODEC = RecordCodecBuilder.mapCodec(i -> i.group((App)DensityFunction.DIRECT_CODEC.fieldOf("input").forGetter(Clamp::input), (App)NOISE_VALUE_CODEC.fieldOf("min").forGetter(Clamp::minValue), (App)NOISE_VALUE_CODEC.fieldOf("max").forGetter(Clamp::maxValue)).apply((Applicative)i, Clamp::new)); public static final KeyDispatchDataCodec CODEC = DensityFunctions.makeCodec(DATA_CODEC); @Override public double transform(double input) { return Mth.clamp(input, this.minValue, this.maxValue); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return new Clamp(this.input.mapAll(visitor), this.minValue, this.maxValue); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } protected record Mapped(Type type, DensityFunction input, double minValue, double maxValue) implements PureTransformer { public static Mapped create(Type type, DensityFunction input) { double minValue = input.minValue(); double maxValue = input.maxValue(); double minImage = Mapped.transform(type, minValue); double maxImage = Mapped.transform(type, maxValue); if (type == Type.INVERT) { if (minValue < 0.0 && maxValue > 0.0) { return new Mapped(type, input, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); } return new Mapped(type, input, maxImage, minImage); } if (type == Type.ABS || type == Type.SQUARE) { return new Mapped(type, input, Math.max(0.0, minValue), Math.max(minImage, maxImage)); } return new Mapped(type, input, minImage, maxImage); } private static double transform(Type type, double input) { return switch (type.ordinal()) { default -> throw new MatchException(null, null); case 0 -> Math.abs(input); case 1 -> input * input; case 2 -> input * input * input; case 3 -> { if (input > 0.0) { yield input; } yield input * 0.5; } case 4 -> { if (input > 0.0) { yield input; } yield input * 0.25; } case 5 -> 1.0 / input; case 6 -> { double c = Mth.clamp(input, -1.0, 1.0); yield c / 2.0 - c * c * c / 24.0; } }; } @Override public double transform(double input) { return Mapped.transform(this.type, input); } @Override public Mapped mapAll(DensityFunction.Visitor visitor) { return Mapped.create(this.type, this.input.mapAll(visitor)); } @Override public KeyDispatchDataCodec codec() { return this.type.codec; } static enum Type implements StringRepresentable { ABS("abs"), SQUARE("square"), CUBE("cube"), HALF_NEGATIVE("half_negative"), QUARTER_NEGATIVE("quarter_negative"), INVERT("invert"), SQUEEZE("squeeze"); private final String name; private final KeyDispatchDataCodec codec = DensityFunctions.singleFunctionArgumentCodec(input -> Mapped.create(this, input), Mapped::input); private Type(String name) { this.name = name; } @Override public String getSerializedName() { return this.name; } } } static interface TwoArgumentSimpleFunction extends DensityFunction { public static final Logger LOGGER = LogUtils.getLogger(); public static TwoArgumentSimpleFunction create(Type type, DensityFunction argument1, DensityFunction argument2) { double maxValue; double min1 = argument1.minValue(); double min2 = argument2.minValue(); double max1 = argument1.maxValue(); double max2 = argument2.maxValue(); if (type == Type.MIN || type == Type.MAX) { boolean secondAlwaysBiggerThanFirst; boolean firstAlwaysBiggerThanSecond = min1 >= max2; boolean bl = secondAlwaysBiggerThanFirst = min2 >= max1; if (firstAlwaysBiggerThanSecond || secondAlwaysBiggerThanFirst) { LOGGER.warn("Creating a {} function between two non-overlapping inputs: {} and {}", new Object[]{type, argument1, argument2}); } } double minValue = switch (type.ordinal()) { default -> throw new MatchException(null, null); case 0 -> min1 + min2; case 3 -> Math.max(min1, min2); case 2 -> Math.min(min1, min2); case 1 -> min1 > 0.0 && min2 > 0.0 ? min1 * min2 : (max1 < 0.0 && max2 < 0.0 ? max1 * max2 : Math.min(min1 * max2, max1 * min2)); }; switch (type.ordinal()) { default: { throw new MatchException(null, null); } case 0: { double d = max1 + max2; break; } case 3: { double d = Math.max(max1, max2); break; } case 2: { double d = Math.min(max1, max2); break; } case 1: { double d = min1 > 0.0 && min2 > 0.0 ? max1 * max2 : (maxValue = max1 < 0.0 && max2 < 0.0 ? min1 * min2 : Math.max(min1 * min2, max1 * max2)); } } if (type == Type.MUL || type == Type.ADD) { if (argument1 instanceof Constant) { Constant constant = (Constant)argument1; return new MulOrAdd(type == Type.ADD ? MulOrAdd.Type.ADD : MulOrAdd.Type.MUL, argument2, minValue, maxValue, constant.value); } if (argument2 instanceof Constant) { Constant constant = (Constant)argument2; return new MulOrAdd(type == Type.ADD ? MulOrAdd.Type.ADD : MulOrAdd.Type.MUL, argument1, minValue, maxValue, constant.value); } } return new Ap2(type, argument1, argument2, minValue, maxValue); } public Type type(); public DensityFunction argument1(); public DensityFunction argument2(); @Override default public KeyDispatchDataCodec codec() { return this.type().codec; } public static enum Type implements StringRepresentable { ADD("add"), MUL("mul"), MIN("min"), MAX("max"); private final KeyDispatchDataCodec codec = DensityFunctions.doubleFunctionArgumentCodec((argument1, argument2) -> TwoArgumentSimpleFunction.create(this, argument1, argument2), TwoArgumentSimpleFunction::argument1, TwoArgumentSimpleFunction::argument2); private final String name; private Type(String name) { this.name = name; } @Override public String getSerializedName() { return this.name; } } } public record Spline(CubicSpline spline) implements DensityFunction { private static final Codec> SPLINE_CODEC = CubicSpline.codec(Coordinate.CODEC); private static final MapCodec DATA_CODEC = SPLINE_CODEC.fieldOf("spline").xmap(Spline::new, Spline::spline); public static final KeyDispatchDataCodec CODEC = DensityFunctions.makeCodec(DATA_CODEC); @Override public double compute(DensityFunction.FunctionContext context) { return this.spline.apply(new Point(context)); } @Override public double minValue() { return this.spline.minValue(); } @Override public double maxValue() { return this.spline.maxValue(); } @Override public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) { contextProvider.fillAllDirectly(output, this); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new Spline(this.spline.mapAll((I c) -> c.mapAll(visitor)))); } @Override public KeyDispatchDataCodec codec() { return CODEC; } public record Point(DensityFunction.FunctionContext context) { } public record Coordinate(Holder function) implements BoundedFloatFunction { public static final Codec CODEC = DensityFunction.CODEC.xmap(Coordinate::new, Coordinate::function); @Override public String toString() { Optional> key = this.function.unwrapKey(); if (key.isPresent()) { ResourceKey name = key.get(); if (name == NoiseRouterData.CONTINENTS) { return "continents"; } if (name == NoiseRouterData.EROSION) { return "erosion"; } if (name == NoiseRouterData.RIDGES) { return "weirdness"; } if (name == NoiseRouterData.RIDGES_FOLDED) { return "ridges"; } } return "Coordinate[" + String.valueOf(this.function) + "]"; } @Override public float apply(Point point) { return (float)this.function.value().compute(point.context()); } @Override public float minValue() { return this.function.isBound() ? (float)this.function.value().minValue() : Float.NEGATIVE_INFINITY; } @Override public float maxValue() { return this.function.isBound() ? (float)this.function.value().maxValue() : Float.POSITIVE_INFINITY; } public Coordinate mapAll(DensityFunction.Visitor visitor) { return new Coordinate(new Holder.Direct(this.function.value().mapAll(visitor))); } } } private record Constant(double value) implements DensityFunction.SimpleFunction { private static final KeyDispatchDataCodec CODEC = DensityFunctions.singleArgumentCodec(NOISE_VALUE_CODEC, Constant::new, Constant::value); private static final Constant ZERO = new Constant(0.0); @Override public double compute(DensityFunction.FunctionContext context) { return this.value; } @Override public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) { Arrays.fill(output, this.value); } @Override public double minValue() { return this.value; } @Override public double maxValue() { return this.value; } @Override public KeyDispatchDataCodec codec() { return CODEC; } } private record YClampedGradient(int fromY, int toY, double fromValue, double toValue) implements DensityFunction.SimpleFunction { private static final MapCodec DATA_CODEC = RecordCodecBuilder.mapCodec(i -> i.group((App)Codec.intRange((int)(DimensionType.MIN_Y * 2), (int)(DimensionType.MAX_Y * 2)).fieldOf("from_y").forGetter(YClampedGradient::fromY), (App)Codec.intRange((int)(DimensionType.MIN_Y * 2), (int)(DimensionType.MAX_Y * 2)).fieldOf("to_y").forGetter(YClampedGradient::toY), (App)NOISE_VALUE_CODEC.fieldOf("from_value").forGetter(YClampedGradient::fromValue), (App)NOISE_VALUE_CODEC.fieldOf("to_value").forGetter(YClampedGradient::toValue)).apply((Applicative)i, YClampedGradient::new)); public static final KeyDispatchDataCodec CODEC = DensityFunctions.makeCodec(DATA_CODEC); @Override public double compute(DensityFunction.FunctionContext context) { return Mth.clampedMap((double)context.blockY(), (double)this.fromY, (double)this.toY, this.fromValue, this.toValue); } @Override public double minValue() { return Math.min(this.fromValue, this.toValue); } @Override public double maxValue() { return Math.max(this.fromValue, this.toValue); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } private record FindTopSurface(DensityFunction density, DensityFunction upperBound, int lowerBound, int cellHeight) implements DensityFunction { private static final MapCodec DATA_CODEC = RecordCodecBuilder.mapCodec(i -> i.group((App)DensityFunction.HOLDER_HELPER_CODEC.fieldOf("density").forGetter(FindTopSurface::density), (App)DensityFunction.HOLDER_HELPER_CODEC.fieldOf("upper_bound").forGetter(FindTopSurface::upperBound), (App)Codec.intRange((int)(DimensionType.MIN_Y * 2), (int)(DimensionType.MAX_Y * 2)).fieldOf("lower_bound").forGetter(FindTopSurface::lowerBound), (App)ExtraCodecs.POSITIVE_INT.fieldOf("cell_height").forGetter(FindTopSurface::cellHeight)).apply((Applicative)i, FindTopSurface::new)); public static final KeyDispatchDataCodec CODEC = DensityFunctions.makeCodec(DATA_CODEC); @Override public double compute(DensityFunction.FunctionContext context) { int topY = Mth.floor(this.upperBound.compute(context) / (double)this.cellHeight) * this.cellHeight; if (topY <= this.lowerBound) { return this.lowerBound; } for (int blockY = topY; blockY >= this.lowerBound; blockY -= this.cellHeight) { DensityFunction.SinglePointContext singlePointContext = new DensityFunction.SinglePointContext(context.blockX(), blockY, context.blockZ()); if (!(this.density.compute(singlePointContext) > 0.0)) continue; return blockY; } return this.lowerBound; } @Override public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) { contextProvider.fillAllDirectly(output, this); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new FindTopSurface(this.density.mapAll(visitor), this.upperBound.mapAll(visitor), this.lowerBound, this.cellHeight)); } @Override public double minValue() { return this.lowerBound; } @Override public double maxValue() { return Math.max((double)this.lowerBound, this.upperBound.maxValue()); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } private record Ap2(TwoArgumentSimpleFunction.Type type, DensityFunction argument1, DensityFunction argument2, double minValue, double maxValue) implements TwoArgumentSimpleFunction { @Override public double compute(DensityFunction.FunctionContext context) { double v1 = this.argument1.compute(context); return switch (this.type.ordinal()) { default -> throw new MatchException(null, null); case 0 -> v1 + this.argument2.compute(context); case 1 -> { if (v1 == 0.0) { yield 0.0; } yield v1 * this.argument2.compute(context); } case 2 -> { if (v1 < this.argument2.minValue()) { yield v1; } yield Math.min(v1, this.argument2.compute(context)); } case 3 -> v1 > this.argument2.maxValue() ? v1 : Math.max(v1, this.argument2.compute(context)); }; } @Override public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) { this.argument1.fillArray(output, contextProvider); switch (this.type.ordinal()) { case 0: { double[] v2 = new double[output.length]; this.argument2.fillArray(v2, contextProvider); for (int i = 0; i < output.length; ++i) { output[i] = output[i] + v2[i]; } break; } case 1: { for (int i = 0; i < output.length; ++i) { double v = output[i]; output[i] = v == 0.0 ? 0.0 : v * this.argument2.compute(contextProvider.forIndex(i)); } break; } case 2: { double min = this.argument2.minValue(); for (int i = 0; i < output.length; ++i) { double v = output[i]; output[i] = v < min ? v : Math.min(v, this.argument2.compute(contextProvider.forIndex(i))); } break; } case 3: { double max = this.argument2.maxValue(); for (int i = 0; i < output.length; ++i) { double v = output[i]; output[i] = v > max ? v : Math.max(v, this.argument2.compute(contextProvider.forIndex(i))); } break; } } } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(TwoArgumentSimpleFunction.create(this.type, this.argument1.mapAll(visitor), this.argument2.mapAll(visitor))); } } private record MulOrAdd(Type specificType, DensityFunction input, double minValue, double maxValue, double argument) implements TwoArgumentSimpleFunction, PureTransformer { @Override public TwoArgumentSimpleFunction.Type type() { return this.specificType == Type.MUL ? TwoArgumentSimpleFunction.Type.MUL : TwoArgumentSimpleFunction.Type.ADD; } @Override public DensityFunction argument1() { return DensityFunctions.constant(this.argument); } @Override public DensityFunction argument2() { return this.input; } @Override public double transform(double input) { return switch (this.specificType.ordinal()) { default -> throw new MatchException(null, null); case 0 -> input * this.argument; case 1 -> input + this.argument; }; } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { double maxValue; double minValue; DensityFunction function = this.input.mapAll(visitor); double min = function.minValue(); double max = function.maxValue(); if (this.specificType == Type.ADD) { minValue = min + this.argument; maxValue = max + this.argument; } else if (this.argument >= 0.0) { minValue = min * this.argument; maxValue = max * this.argument; } else { minValue = max * this.argument; maxValue = min * this.argument; } return new MulOrAdd(this.specificType, function, minValue, maxValue, this.argument); } static enum Type { MUL, ADD; } } static interface ShiftNoise extends DensityFunction { public DensityFunction.NoiseHolder offsetNoise(); @Override default public double minValue() { return -this.maxValue(); } @Override default public double maxValue() { return this.offsetNoise().maxValue() * 4.0; } default public double compute(double localX, double localY, double localZ) { return this.offsetNoise().getValue(localX * 0.25, localY * 0.25, localZ * 0.25) * 4.0; } @Override default public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) { contextProvider.fillAllDirectly(output, this); } } public static interface MarkerOrMarked extends DensityFunction { public Marker.Type type(); public DensityFunction wrapped(); @Override default public KeyDispatchDataCodec codec() { return this.type().codec; } @Override default public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new Marker(this.type(), this.wrapped().mapAll(visitor))); } } @VisibleForDebug public record HolderHolder(Holder function) implements DensityFunction { @Override public double compute(DensityFunction.FunctionContext context) { return this.function.value().compute(context); } @Override public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) { this.function.value().fillArray(output, contextProvider); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new HolderHolder(new Holder.Direct(this.function.value().mapAll(visitor)))); } @Override public double minValue() { return this.function.isBound() ? this.function.value().minValue() : Double.NEGATIVE_INFINITY; } @Override public double maxValue() { return this.function.isBound() ? this.function.value().maxValue() : Double.POSITIVE_INFINITY; } @Override public KeyDispatchDataCodec codec() { throw new UnsupportedOperationException("Calling .codec() on HolderHolder"); } } public static interface BeardifierOrMarker extends DensityFunction.SimpleFunction { public static final KeyDispatchDataCodec CODEC = KeyDispatchDataCodec.of(MapCodec.unit((Object)BeardifierMarker.INSTANCE)); @Override default public KeyDispatchDataCodec codec() { return CODEC; } } private static interface PureTransformer extends DensityFunction { public DensityFunction input(); @Override default public double compute(DensityFunction.FunctionContext context) { return this.transform(this.input().compute(context)); } @Override default public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) { this.input().fillArray(output, contextProvider); for (int i = 0; i < output.length; ++i) { output[i] = this.transform(output[i]); } } public double transform(double var1); } private static interface TransformerWithContext extends DensityFunction { public DensityFunction input(); @Override default public double compute(DensityFunction.FunctionContext context) { return this.transform(context, this.input().compute(context)); } @Override default public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) { this.input().fillArray(output, contextProvider); for (int i = 0; i < output.length; ++i) { output[i] = this.transform(contextProvider.forIndex(i), output[i]); } } public double transform(DensityFunction.FunctionContext var1, double var2); } }