minecraft-src/net/minecraft/world/level/levelgen/DensityFunctions.java
2025-07-04 03:45:38 +03:00

1206 lines
44 KiB
Java

package net.minecraft.world.level.levelgen;
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 java.util.Arrays;
import java.util.function.BiFunction;
import java.util.function.Function;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.core.Holder.Direct;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.util.CubicSpline;
import net.minecraft.util.KeyDispatchDataCodec;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.util.VisibleForDebug;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.levelgen.DensityFunctions.Marker.Type;
import net.minecraft.world.level.levelgen.DensityFunctions.Spline.Coordinate;
import net.minecraft.world.level.levelgen.DensityFunctions.Spline.Point;
import net.minecraft.world.level.levelgen.DensityFunctions.WeirdScaledSampler.RarityValueMapper;
import net.minecraft.world.level.levelgen.synth.BlendedNoise;
import net.minecraft.world.level.levelgen.synth.SimplexNoise;
import net.minecraft.world.level.levelgen.synth.NormalNoise.NoiseParameters;
import org.slf4j.Logger;
public final class DensityFunctions {
private static final Codec<DensityFunction> CODEC = BuiltInRegistries.DENSITY_FUNCTION_TYPE
.byNameCodec()
.dispatch(densityFunction -> densityFunction.codec().codec(), Function.identity());
protected static final double MAX_REASONABLE_NOISE_VALUE = 1000000.0;
static final Codec<Double> NOISE_VALUE_CODEC = Codec.doubleRange(-1000000.0, 1000000.0);
public static final Codec<DensityFunction> DIRECT_CODEC = Codec.either(NOISE_VALUE_CODEC, CODEC)
.xmap(
either -> either.map(DensityFunctions::constant, Function.identity()),
densityFunction -> densityFunction instanceof DensityFunctions.Constant constant ? Either.left(constant.value()) : Either.right(densityFunction)
);
public static MapCodec<? extends DensityFunction> bootstrap(Registry<MapCodec<? extends DensityFunction>> registry) {
register(registry, "blend_alpha", DensityFunctions.BlendAlpha.CODEC);
register(registry, "blend_offset", DensityFunctions.BlendOffset.CODEC);
register(registry, "beardifier", DensityFunctions.BeardifierMarker.CODEC);
register(registry, "old_blended_noise", BlendedNoise.CODEC);
for (Type type : Type.values()) {
register(registry, type.getSerializedName(), type.codec);
}
register(registry, "noise", DensityFunctions.Noise.CODEC);
register(registry, "end_islands", DensityFunctions.EndIslandDensityFunction.CODEC);
register(registry, "weird_scaled_sampler", DensityFunctions.WeirdScaledSampler.CODEC);
register(registry, "shifted_noise", DensityFunctions.ShiftedNoise.CODEC);
register(registry, "range_choice", DensityFunctions.RangeChoice.CODEC);
register(registry, "shift_a", DensityFunctions.ShiftA.CODEC);
register(registry, "shift_b", DensityFunctions.ShiftB.CODEC);
register(registry, "shift", DensityFunctions.Shift.CODEC);
register(registry, "blend_density", DensityFunctions.BlendDensity.CODEC);
register(registry, "clamp", DensityFunctions.Clamp.CODEC);
for (net.minecraft.world.level.levelgen.DensityFunctions.Mapped.Type type2 : net.minecraft.world.level.levelgen.DensityFunctions.Mapped.Type.values()) {
register(registry, type2.getSerializedName(), type2.codec);
}
for (net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type type3 : net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.values()) {
register(registry, type3.getSerializedName(), type3.codec);
}
register(registry, "spline", DensityFunctions.Spline.CODEC);
register(registry, "constant", DensityFunctions.Constant.CODEC);
return register(registry, "y_clamped_gradient", DensityFunctions.YClampedGradient.CODEC);
}
private static MapCodec<? extends DensityFunction> register(
Registry<MapCodec<? extends DensityFunction>> registry, String name, KeyDispatchDataCodec<? extends DensityFunction> codec
) {
return Registry.register(registry, name, codec.codec());
}
static <A, O> KeyDispatchDataCodec<O> singleArgumentCodec(Codec<A> codec, Function<A, O> fromFunction, Function<O, A> toFunction) {
return KeyDispatchDataCodec.of(codec.fieldOf("argument").xmap(fromFunction, toFunction));
}
static <O> KeyDispatchDataCodec<O> singleFunctionArgumentCodec(Function<DensityFunction, O> fromFunction, Function<O, DensityFunction> toFunction) {
return singleArgumentCodec(DensityFunction.HOLDER_HELPER_CODEC, fromFunction, toFunction);
}
static <O> KeyDispatchDataCodec<O> doubleFunctionArgumentCodec(
BiFunction<DensityFunction, DensityFunction, O> fromFunction, Function<O, DensityFunction> primary, Function<O, DensityFunction> secondary
) {
return KeyDispatchDataCodec.of(
RecordCodecBuilder.mapCodec(
instance -> instance.group(
DensityFunction.HOLDER_HELPER_CODEC.fieldOf("argument1").forGetter(primary),
DensityFunction.HOLDER_HELPER_CODEC.fieldOf("argument2").forGetter(secondary)
)
.apply(instance, fromFunction)
)
);
}
static <O> KeyDispatchDataCodec<O> makeCodec(MapCodec<O> mapCodec) {
return KeyDispatchDataCodec.of(mapCodec);
}
private DensityFunctions() {
}
public static DensityFunction interpolated(DensityFunction wrapped) {
return new DensityFunctions.Marker(Type.Interpolated, wrapped);
}
public static DensityFunction flatCache(DensityFunction wrapped) {
return new DensityFunctions.Marker(Type.FlatCache, wrapped);
}
public static DensityFunction cache2d(DensityFunction wrapped) {
return new DensityFunctions.Marker(Type.Cache2D, wrapped);
}
public static DensityFunction cacheOnce(DensityFunction wrapped) {
return new DensityFunctions.Marker(Type.CacheOnce, wrapped);
}
public static DensityFunction cacheAllInCell(DensityFunction wrapped) {
return new DensityFunctions.Marker(Type.CacheAllInCell, wrapped);
}
public static DensityFunction mappedNoise(Holder<NoiseParameters> noiseData, @Deprecated double xzScale, double yScale, double fromY, double toY) {
return mapFromUnitTo(new DensityFunctions.Noise(new DensityFunction.NoiseHolder(noiseData), xzScale, yScale), fromY, toY);
}
public static DensityFunction mappedNoise(Holder<NoiseParameters> noiseData, double yScale, double fromY, double toY) {
return mappedNoise(noiseData, 1.0, yScale, fromY, toY);
}
public static DensityFunction mappedNoise(Holder<NoiseParameters> noiseData, double fromY, double toY) {
return mappedNoise(noiseData, 1.0, 1.0, fromY, toY);
}
public static DensityFunction shiftedNoise2d(DensityFunction shiftX, DensityFunction shiftZ, double xzScale, Holder<NoiseParameters> noiseData) {
return new DensityFunctions.ShiftedNoise(shiftX, zero(), shiftZ, xzScale, 0.0, new DensityFunction.NoiseHolder(noiseData));
}
public static DensityFunction noise(Holder<NoiseParameters> noiseData) {
return noise(noiseData, 1.0, 1.0);
}
public static DensityFunction noise(Holder<NoiseParameters> noiseData, double xzScale, double yScale) {
return new DensityFunctions.Noise(new DensityFunction.NoiseHolder(noiseData), xzScale, yScale);
}
public static DensityFunction noise(Holder<NoiseParameters> noiseData, double yScale) {
return noise(noiseData, 1.0, yScale);
}
public static DensityFunction rangeChoice(
DensityFunction input, double minInclusive, double maxExclusive, DensityFunction whenInRange, DensityFunction whenOutOfRange
) {
return new DensityFunctions.RangeChoice(input, minInclusive, maxExclusive, whenInRange, whenOutOfRange);
}
public static DensityFunction shiftA(Holder<NoiseParameters> noiseData) {
return new DensityFunctions.ShiftA(new DensityFunction.NoiseHolder(noiseData));
}
public static DensityFunction shiftB(Holder<NoiseParameters> noiseData) {
return new DensityFunctions.ShiftB(new DensityFunction.NoiseHolder(noiseData));
}
public static DensityFunction shift(Holder<NoiseParameters> noiseData) {
return new DensityFunctions.Shift(new DensityFunction.NoiseHolder(noiseData));
}
public static DensityFunction blendDensity(DensityFunction input) {
return new DensityFunctions.BlendDensity(input);
}
public static DensityFunction endIslands(long seed) {
return new DensityFunctions.EndIslandDensityFunction(seed);
}
public static DensityFunction weirdScaledSampler(DensityFunction input, Holder<NoiseParameters> noiseData, RarityValueMapper rarityValueMapper) {
return new DensityFunctions.WeirdScaledSampler(input, new DensityFunction.NoiseHolder(noiseData), rarityValueMapper);
}
public static DensityFunction add(DensityFunction argument1, DensityFunction argument2) {
return DensityFunctions.TwoArgumentSimpleFunction.create(
net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.ADD, argument1, argument2
);
}
public static DensityFunction mul(DensityFunction argument1, DensityFunction argument2) {
return DensityFunctions.TwoArgumentSimpleFunction.create(
net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.MUL, argument1, argument2
);
}
public static DensityFunction min(DensityFunction argument1, DensityFunction argument2) {
return DensityFunctions.TwoArgumentSimpleFunction.create(
net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.MIN, argument1, argument2
);
}
public static DensityFunction max(DensityFunction argument1, DensityFunction argument2) {
return DensityFunctions.TwoArgumentSimpleFunction.create(
net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.MAX, argument1, argument2
);
}
public static DensityFunction spline(CubicSpline<Point, Coordinate> spline) {
return new DensityFunctions.Spline(spline);
}
public static DensityFunction zero() {
return DensityFunctions.Constant.ZERO;
}
public static DensityFunction constant(double value) {
return new DensityFunctions.Constant(value);
}
public static DensityFunction yClampedGradient(int fromY, int toY, double fromValue, double toValue) {
return new DensityFunctions.YClampedGradient(fromY, toY, fromValue, toValue);
}
public static DensityFunction map(DensityFunction input, net.minecraft.world.level.levelgen.DensityFunctions.Mapped.Type type) {
return DensityFunctions.Mapped.create(type, input);
}
private static DensityFunction mapFromUnitTo(DensityFunction densityFunction, double fromY, double toY) {
double d = (fromY + toY) * 0.5;
double e = (toY - fromY) * 0.5;
return add(constant(d), mul(constant(e), densityFunction));
}
public static DensityFunction blendAlpha() {
return DensityFunctions.BlendAlpha.INSTANCE;
}
public static DensityFunction blendOffset() {
return DensityFunctions.BlendOffset.INSTANCE;
}
public static DensityFunction lerp(DensityFunction deltaFunction, DensityFunction minFunction, DensityFunction maxFunction) {
if (minFunction instanceof DensityFunctions.Constant constant) {
return lerp(deltaFunction, constant.value, maxFunction);
} else {
DensityFunction densityFunction = cacheOnce(deltaFunction);
DensityFunction densityFunction2 = add(mul(densityFunction, constant(-1.0)), constant(1.0));
return add(mul(minFunction, densityFunction2), mul(maxFunction, densityFunction));
}
}
public static DensityFunction lerp(DensityFunction deltaFunction, double min, DensityFunction maxFunction) {
return add(mul(deltaFunction, add(maxFunction, constant(-min))), constant(min));
}
record Ap2(
net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type type,
DensityFunction argument1,
DensityFunction argument2,
double minValue,
double maxValue
) implements DensityFunctions.TwoArgumentSimpleFunction {
// $VF: Unable to simplify switch-on-enum, as the enum class was not able to be found.
// Please report this to the Vineflower issue tracker, at https://github.com/Vineflower/vineflower/issues with a copy of the class file (if you have the rights to distribute it!)
@Override
public double compute(DensityFunction.FunctionContext context) {
double d = this.argument1.compute(context);
return switch (this.type.ordinal()) {
case 0 -> d + this.argument2.compute(context);
case 1 -> d == 0.0 ? 0.0 : d * this.argument2.compute(context);
case 2 -> d < this.argument2.minValue() ? d : Math.min(d, this.argument2.compute(context));
case 3 -> d > this.argument2.maxValue() ? d : Math.max(d, this.argument2.compute(context));
default -> throw new MatchException(null, null);
};
}
// $VF: Unable to simplify switch-on-enum, as the enum class was not able to be found.
// Please report this to the Vineflower issue tracker, at https://github.com/Vineflower/vineflower/issues with a copy of the class file (if you have the rights to distribute it!)
@Override
public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
this.argument1.fillArray(array, contextProvider);
switch (this.type.ordinal()) {
case 0:
double[] ds = new double[array.length];
this.argument2.fillArray(ds, contextProvider);
for (int i = 0; i < array.length; i++) {
array[i] += ds[i];
}
break;
case 1:
for (int j = 0; j < array.length; j++) {
double d = array[j];
array[j] = d == 0.0 ? 0.0 : d * this.argument2.compute(contextProvider.forIndex(j));
}
break;
case 2:
double e = this.argument2.minValue();
for (int k = 0; k < array.length; k++) {
double f = array[k];
array[k] = f < e ? f : Math.min(f, this.argument2.compute(contextProvider.forIndex(k)));
}
break;
case 3:
double e = this.argument2.maxValue();
for (int k = 0; k < array.length; k++) {
double f = array[k];
array[k] = f > e ? f : Math.max(f, this.argument2.compute(contextProvider.forIndex(k)));
}
}
}
@Override
public DensityFunction mapAll(DensityFunction.Visitor visitor) {
return visitor.apply(DensityFunctions.TwoArgumentSimpleFunction.create(this.type, this.argument1.mapAll(visitor), this.argument2.mapAll(visitor)));
}
}
protected static enum BeardifierMarker implements DensityFunctions.BeardifierOrMarker {
INSTANCE;
@Override
public double compute(DensityFunction.FunctionContext context) {
return 0.0;
}
@Override
public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
Arrays.fill(array, 0.0);
}
@Override
public double minValue() {
return 0.0;
}
@Override
public double maxValue() {
return 0.0;
}
}
public interface BeardifierOrMarker extends DensityFunction.SimpleFunction {
KeyDispatchDataCodec<DensityFunction> CODEC = KeyDispatchDataCodec.of(MapCodec.unit(DensityFunctions.BeardifierMarker.INSTANCE));
@Override
default KeyDispatchDataCodec<? extends DensityFunction> codec() {
return CODEC;
}
}
protected static enum BlendAlpha implements DensityFunction.SimpleFunction {
INSTANCE;
public static final KeyDispatchDataCodec<DensityFunction> CODEC = KeyDispatchDataCodec.of(MapCodec.unit(INSTANCE));
@Override
public double compute(DensityFunction.FunctionContext context) {
return 1.0;
}
@Override
public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
Arrays.fill(array, 1.0);
}
@Override
public double minValue() {
return 1.0;
}
@Override
public double maxValue() {
return 1.0;
}
@Override
public KeyDispatchDataCodec<? extends DensityFunction> codec() {
return CODEC;
}
}
record BlendDensity(DensityFunction input) implements DensityFunctions.TransformerWithContext {
static final KeyDispatchDataCodec<DensityFunctions.BlendDensity> CODEC = DensityFunctions.singleFunctionArgumentCodec(
DensityFunctions.BlendDensity::new, DensityFunctions.BlendDensity::input
);
@Override
public double transform(DensityFunction.FunctionContext context, double value) {
return context.getBlender().blendDensity(context, value);
}
@Override
public DensityFunction mapAll(DensityFunction.Visitor visitor) {
return visitor.apply(new DensityFunctions.BlendDensity(this.input.mapAll(visitor)));
}
@Override
public double minValue() {
return Double.NEGATIVE_INFINITY;
}
@Override
public double maxValue() {
return Double.POSITIVE_INFINITY;
}
@Override
public KeyDispatchDataCodec<? extends DensityFunction> codec() {
return CODEC;
}
}
protected static enum BlendOffset implements DensityFunction.SimpleFunction {
INSTANCE;
public static final KeyDispatchDataCodec<DensityFunction> CODEC = KeyDispatchDataCodec.of(MapCodec.unit(INSTANCE));
@Override
public double compute(DensityFunction.FunctionContext context) {
return 0.0;
}
@Override
public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
Arrays.fill(array, 0.0);
}
@Override
public double minValue() {
return 0.0;
}
@Override
public double maxValue() {
return 0.0;
}
@Override
public KeyDispatchDataCodec<? extends DensityFunction> codec() {
return CODEC;
}
}
protected record Clamp(DensityFunction input, double minValue, double maxValue) implements DensityFunctions.PureTransformer {
private static final MapCodec<DensityFunctions.Clamp> DATA_CODEC = RecordCodecBuilder.mapCodec(
instance -> instance.group(
DensityFunction.DIRECT_CODEC.fieldOf("input").forGetter(DensityFunctions.Clamp::input),
DensityFunctions.NOISE_VALUE_CODEC.fieldOf("min").forGetter(DensityFunctions.Clamp::minValue),
DensityFunctions.NOISE_VALUE_CODEC.fieldOf("max").forGetter(DensityFunctions.Clamp::maxValue)
)
.apply(instance, DensityFunctions.Clamp::new)
);
public static final KeyDispatchDataCodec<DensityFunctions.Clamp> CODEC = DensityFunctions.makeCodec(DATA_CODEC);
@Override
public double transform(double value) {
return Mth.clamp(value, this.minValue, this.maxValue);
}
@Override
public DensityFunction mapAll(DensityFunction.Visitor visitor) {
return new DensityFunctions.Clamp(this.input.mapAll(visitor), this.minValue, this.maxValue);
}
@Override
public KeyDispatchDataCodec<? extends DensityFunction> codec() {
return CODEC;
}
}
record Constant(double value) implements DensityFunction.SimpleFunction {
static final KeyDispatchDataCodec<DensityFunctions.Constant> CODEC = DensityFunctions.singleArgumentCodec(
DensityFunctions.NOISE_VALUE_CODEC, DensityFunctions.Constant::new, DensityFunctions.Constant::value
);
static final DensityFunctions.Constant ZERO = new DensityFunctions.Constant(0.0);
@Override
public double compute(DensityFunction.FunctionContext context) {
return this.value;
}
@Override
public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
Arrays.fill(array, this.value);
}
@Override
public double minValue() {
return this.value;
}
@Override
public double maxValue() {
return this.value;
}
@Override
public KeyDispatchDataCodec<? extends DensityFunction> codec() {
return CODEC;
}
}
protected static final class EndIslandDensityFunction implements DensityFunction.SimpleFunction {
public static final KeyDispatchDataCodec<DensityFunctions.EndIslandDensityFunction> CODEC = KeyDispatchDataCodec.of(
MapCodec.unit(new DensityFunctions.EndIslandDensityFunction(0L))
);
private static final float ISLAND_THRESHOLD = -0.9F;
private final SimplexNoise islandNoise;
public EndIslandDensityFunction(long seed) {
RandomSource randomSource = new LegacyRandomSource(seed);
randomSource.consumeCount(17292);
this.islandNoise = new SimplexNoise(randomSource);
}
private static float getHeightValue(SimplexNoise noise, int x, int z) {
int i = x / 2;
int j = z / 2;
int k = x % 2;
int l = z % 2;
float f = 100.0F - Mth.sqrt(x * x + z * z) * 8.0F;
f = Mth.clamp(f, -100.0F, 80.0F);
for (int m = -12; m <= 12; m++) {
for (int n = -12; n <= 12; n++) {
long o = i + m;
long p = j + n;
if (o * o + p * p > 4096L && noise.getValue(o, p) < -0.9F) {
float g = (Mth.abs((float)o) * 3439.0F + Mth.abs((float)p) * 147.0F) % 13.0F + 9.0F;
float h = k - m * 2;
float q = l - n * 2;
float r = 100.0F - Mth.sqrt(h * h + q * q) * g;
r = Mth.clamp(r, -100.0F, 80.0F);
f = Math.max(f, r);
}
}
}
return f;
}
@Override
public double compute(DensityFunction.FunctionContext context) {
return (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<? extends DensityFunction> codec() {
return CODEC;
}
}
@VisibleForDebug
public record HolderHolder(Holder<DensityFunction> function) implements DensityFunction {
@Override
public double compute(DensityFunction.FunctionContext context) {
return this.function.value().compute(context);
}
@Override
public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
this.function.value().fillArray(array, contextProvider);
}
@Override
public DensityFunction mapAll(DensityFunction.Visitor visitor) {
return visitor.apply(new DensityFunctions.HolderHolder(new 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<? extends DensityFunction> codec() {
throw new UnsupportedOperationException("Calling .codec() on HolderHolder");
}
}
protected record Mapped(net.minecraft.world.level.levelgen.DensityFunctions.Mapped.Type type, DensityFunction input, double minValue, double maxValue)
implements DensityFunctions.PureTransformer {
public static DensityFunctions.Mapped create(net.minecraft.world.level.levelgen.DensityFunctions.Mapped.Type type, DensityFunction input) {
double d = input.minValue();
double e = transform(type, d);
double f = transform(type, input.maxValue());
return type != net.minecraft.world.level.levelgen.DensityFunctions.Mapped.Type.ABS
&& type != net.minecraft.world.level.levelgen.DensityFunctions.Mapped.Type.SQUARE
? new DensityFunctions.Mapped(type, input, e, f)
: new DensityFunctions.Mapped(type, input, Math.max(0.0, d), Math.max(e, f));
}
// $VF: Unable to simplify switch-on-enum, as the enum class was not able to be found.
// Please report this to the Vineflower issue tracker, at https://github.com/Vineflower/vineflower/issues with a copy of the class file (if you have the rights to distribute it!)
private static double transform(net.minecraft.world.level.levelgen.DensityFunctions.Mapped.Type type, double value) {
return switch (type.ordinal()) {
case 0 -> Math.abs(value);
case 1 -> value * value;
case 2 -> value * value * value;
case 3 -> value > 0.0 ? value : value * 0.5;
case 4 -> value > 0.0 ? value : value * 0.25;
case 5 -> {
double d = Mth.clamp(value, -1.0, 1.0);
yield d / 2.0 - d * d * d / 24.0;
}
default -> throw new MatchException(null, null);
};
}
@Override
public double transform(double value) {
return transform(this.type, value);
}
public DensityFunctions.Mapped mapAll(DensityFunction.Visitor visitor) {
return create(this.type, this.input.mapAll(visitor));
}
@Override
public KeyDispatchDataCodec<? extends DensityFunction> codec() {
return this.type.codec;
}
}
protected record Marker(Type type, DensityFunction wrapped) implements DensityFunctions.MarkerOrMarked {
@Override
public double compute(DensityFunction.FunctionContext context) {
return this.wrapped.compute(context);
}
@Override
public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
this.wrapped.fillArray(array, contextProvider);
}
@Override
public double minValue() {
return this.wrapped.minValue();
}
@Override
public double maxValue() {
return this.wrapped.maxValue();
}
}
public interface MarkerOrMarked extends DensityFunction {
Type type();
DensityFunction wrapped();
@Override
default KeyDispatchDataCodec<? extends DensityFunction> codec() {
return this.type().codec;
}
@Override
default DensityFunction mapAll(DensityFunction.Visitor visitor) {
return visitor.apply(new DensityFunctions.Marker(this.type(), this.wrapped().mapAll(visitor)));
}
}
record MulOrAdd(
net.minecraft.world.level.levelgen.DensityFunctions.MulOrAdd.Type specificType, DensityFunction input, double minValue, double maxValue, double argument
) implements DensityFunctions.PureTransformer, DensityFunctions.TwoArgumentSimpleFunction {
@Override
public net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type type() {
return this.specificType == net.minecraft.world.level.levelgen.DensityFunctions.MulOrAdd.Type.MUL
? net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.MUL
: net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.ADD;
}
@Override
public DensityFunction argument1() {
return DensityFunctions.constant(this.argument);
}
@Override
public DensityFunction argument2() {
return this.input;
}
// $VF: Unable to simplify switch-on-enum, as the enum class was not able to be found.
// Please report this to the Vineflower issue tracker, at https://github.com/Vineflower/vineflower/issues with a copy of the class file (if you have the rights to distribute it!)
@Override
public double transform(double value) {
return switch (this.specificType.ordinal()) {
case 0 -> value * this.argument;
case 1 -> value + this.argument;
default -> throw new MatchException(null, null);
};
}
@Override
public DensityFunction mapAll(DensityFunction.Visitor visitor) {
DensityFunction densityFunction = this.input.mapAll(visitor);
double d = densityFunction.minValue();
double e = densityFunction.maxValue();
double f;
double g;
if (this.specificType == net.minecraft.world.level.levelgen.DensityFunctions.MulOrAdd.Type.ADD) {
f = d + this.argument;
g = e + this.argument;
} else if (this.argument >= 0.0) {
f = d * this.argument;
g = e * this.argument;
} else {
f = e * this.argument;
g = d * this.argument;
}
return new DensityFunctions.MulOrAdd(this.specificType, densityFunction, f, g, this.argument);
}
}
protected record Noise(DensityFunction.NoiseHolder noise, @Deprecated double xzScale, double yScale) implements DensityFunction {
public static final MapCodec<DensityFunctions.Noise> DATA_CODEC = RecordCodecBuilder.mapCodec(
instance -> instance.group(
DensityFunction.NoiseHolder.CODEC.fieldOf("noise").forGetter(DensityFunctions.Noise::noise),
Codec.DOUBLE.fieldOf("xz_scale").forGetter(DensityFunctions.Noise::xzScale),
Codec.DOUBLE.fieldOf("y_scale").forGetter(DensityFunctions.Noise::yScale)
)
.apply(instance, DensityFunctions.Noise::new)
);
public static final KeyDispatchDataCodec<DensityFunctions.Noise> CODEC = DensityFunctions.makeCodec(DATA_CODEC);
@Override
public double compute(DensityFunction.FunctionContext context) {
return this.noise.getValue(context.blockX() * this.xzScale, context.blockY() * this.yScale, context.blockZ() * this.xzScale);
}
@Override
public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
contextProvider.fillAllDirectly(array, this);
}
@Override
public DensityFunction mapAll(DensityFunction.Visitor visitor) {
return visitor.apply(new DensityFunctions.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<? extends DensityFunction> codec() {
return CODEC;
}
}
interface PureTransformer extends DensityFunction {
DensityFunction input();
@Override
default double compute(DensityFunction.FunctionContext context) {
return this.transform(this.input().compute(context));
}
@Override
default void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
this.input().fillArray(array, contextProvider);
for (int i = 0; i < array.length; i++) {
array[i] = this.transform(array[i]);
}
}
double transform(double value);
}
record RangeChoice(DensityFunction input, double minInclusive, double maxExclusive, DensityFunction whenInRange, DensityFunction whenOutOfRange)
implements DensityFunction {
public static final MapCodec<DensityFunctions.RangeChoice> DATA_CODEC = RecordCodecBuilder.mapCodec(
instance -> instance.group(
DensityFunction.HOLDER_HELPER_CODEC.fieldOf("input").forGetter(DensityFunctions.RangeChoice::input),
DensityFunctions.NOISE_VALUE_CODEC.fieldOf("min_inclusive").forGetter(DensityFunctions.RangeChoice::minInclusive),
DensityFunctions.NOISE_VALUE_CODEC.fieldOf("max_exclusive").forGetter(DensityFunctions.RangeChoice::maxExclusive),
DensityFunction.HOLDER_HELPER_CODEC.fieldOf("when_in_range").forGetter(DensityFunctions.RangeChoice::whenInRange),
DensityFunction.HOLDER_HELPER_CODEC.fieldOf("when_out_of_range").forGetter(DensityFunctions.RangeChoice::whenOutOfRange)
)
.apply(instance, DensityFunctions.RangeChoice::new)
);
public static final KeyDispatchDataCodec<DensityFunctions.RangeChoice> CODEC = DensityFunctions.makeCodec(DATA_CODEC);
@Override
public double compute(DensityFunction.FunctionContext context) {
double d = this.input.compute(context);
return d >= this.minInclusive && d < this.maxExclusive ? this.whenInRange.compute(context) : this.whenOutOfRange.compute(context);
}
@Override
public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
this.input.fillArray(array, contextProvider);
for (int i = 0; i < array.length; i++) {
double d = array[i];
if (d >= this.minInclusive && d < this.maxExclusive) {
array[i] = this.whenInRange.compute(contextProvider.forIndex(i));
} else {
array[i] = this.whenOutOfRange.compute(contextProvider.forIndex(i));
}
}
}
@Override
public DensityFunction mapAll(DensityFunction.Visitor visitor) {
return visitor.apply(
new DensityFunctions.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<? extends DensityFunction> codec() {
return CODEC;
}
}
protected record Shift(DensityFunction.NoiseHolder offsetNoise) implements DensityFunctions.ShiftNoise {
static final KeyDispatchDataCodec<DensityFunctions.Shift> CODEC = DensityFunctions.singleArgumentCodec(
DensityFunction.NoiseHolder.CODEC, DensityFunctions.Shift::new, DensityFunctions.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 DensityFunctions.Shift(visitor.visitNoise(this.offsetNoise)));
}
@Override
public KeyDispatchDataCodec<? extends DensityFunction> codec() {
return CODEC;
}
}
protected record ShiftA(DensityFunction.NoiseHolder offsetNoise) implements DensityFunctions.ShiftNoise {
static final KeyDispatchDataCodec<DensityFunctions.ShiftA> CODEC = DensityFunctions.singleArgumentCodec(
DensityFunction.NoiseHolder.CODEC, DensityFunctions.ShiftA::new, DensityFunctions.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 DensityFunctions.ShiftA(visitor.visitNoise(this.offsetNoise)));
}
@Override
public KeyDispatchDataCodec<? extends DensityFunction> codec() {
return CODEC;
}
}
protected record ShiftB(DensityFunction.NoiseHolder offsetNoise) implements DensityFunctions.ShiftNoise {
static final KeyDispatchDataCodec<DensityFunctions.ShiftB> CODEC = DensityFunctions.singleArgumentCodec(
DensityFunction.NoiseHolder.CODEC, DensityFunctions.ShiftB::new, DensityFunctions.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 DensityFunctions.ShiftB(visitor.visitNoise(this.offsetNoise)));
}
@Override
public KeyDispatchDataCodec<? extends DensityFunction> codec() {
return CODEC;
}
}
interface ShiftNoise extends DensityFunction {
DensityFunction.NoiseHolder offsetNoise();
@Override
default double minValue() {
return -this.maxValue();
}
@Override
default double maxValue() {
return this.offsetNoise().maxValue() * 4.0;
}
default double compute(double x, double y, double z) {
return this.offsetNoise().getValue(x * 0.25, y * 0.25, z * 0.25) * 4.0;
}
@Override
default void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
contextProvider.fillAllDirectly(array, this);
}
}
protected record ShiftedNoise(
DensityFunction shiftX, DensityFunction shiftY, DensityFunction shiftZ, double xzScale, double yScale, DensityFunction.NoiseHolder noise
) implements DensityFunction {
private static final MapCodec<DensityFunctions.ShiftedNoise> DATA_CODEC = RecordCodecBuilder.mapCodec(
instance -> instance.group(
DensityFunction.HOLDER_HELPER_CODEC.fieldOf("shift_x").forGetter(DensityFunctions.ShiftedNoise::shiftX),
DensityFunction.HOLDER_HELPER_CODEC.fieldOf("shift_y").forGetter(DensityFunctions.ShiftedNoise::shiftY),
DensityFunction.HOLDER_HELPER_CODEC.fieldOf("shift_z").forGetter(DensityFunctions.ShiftedNoise::shiftZ),
Codec.DOUBLE.fieldOf("xz_scale").forGetter(DensityFunctions.ShiftedNoise::xzScale),
Codec.DOUBLE.fieldOf("y_scale").forGetter(DensityFunctions.ShiftedNoise::yScale),
DensityFunction.NoiseHolder.CODEC.fieldOf("noise").forGetter(DensityFunctions.ShiftedNoise::noise)
)
.apply(instance, DensityFunctions.ShiftedNoise::new)
);
public static final KeyDispatchDataCodec<DensityFunctions.ShiftedNoise> CODEC = DensityFunctions.makeCodec(DATA_CODEC);
@Override
public double compute(DensityFunction.FunctionContext context) {
double d = context.blockX() * this.xzScale + this.shiftX.compute(context);
double e = context.blockY() * this.yScale + this.shiftY.compute(context);
double f = context.blockZ() * this.xzScale + this.shiftZ.compute(context);
return this.noise.getValue(d, e, f);
}
@Override
public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
contextProvider.fillAllDirectly(array, this);
}
@Override
public DensityFunction mapAll(DensityFunction.Visitor visitor) {
return visitor.apply(
new DensityFunctions.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<? extends DensityFunction> codec() {
return CODEC;
}
}
public record Spline(CubicSpline<Point, Coordinate> spline) implements DensityFunction {
private static final Codec<CubicSpline<Point, Coordinate>> SPLINE_CODEC = CubicSpline.codec(Coordinate.CODEC);
private static final MapCodec<DensityFunctions.Spline> DATA_CODEC = SPLINE_CODEC.fieldOf("spline")
.xmap(DensityFunctions.Spline::new, DensityFunctions.Spline::spline);
public static final KeyDispatchDataCodec<DensityFunctions.Spline> 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[] array, DensityFunction.ContextProvider contextProvider) {
contextProvider.fillAllDirectly(array, this);
}
@Override
public DensityFunction mapAll(DensityFunction.Visitor visitor) {
return visitor.apply(new DensityFunctions.Spline(this.spline.mapAll(coordinate -> coordinate.mapAll(visitor))));
}
@Override
public KeyDispatchDataCodec<? extends DensityFunction> codec() {
return CODEC;
}
}
interface TransformerWithContext extends DensityFunction {
DensityFunction input();
@Override
default double compute(DensityFunction.FunctionContext context) {
return this.transform(context, this.input().compute(context));
}
@Override
default void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
this.input().fillArray(array, contextProvider);
for (int i = 0; i < array.length; i++) {
array[i] = this.transform(contextProvider.forIndex(i), array[i]);
}
}
double transform(DensityFunction.FunctionContext context, double value);
}
interface TwoArgumentSimpleFunction extends DensityFunction {
Logger LOGGER = LogUtils.getLogger();
// $VF: Unable to simplify switch-on-enum, as the enum class was not able to be found.
// Please report this to the Vineflower issue tracker, at https://github.com/Vineflower/vineflower/issues with a copy of the class file (if you have the rights to distribute it!)
static DensityFunctions.TwoArgumentSimpleFunction create(
net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type type, DensityFunction argument1, DensityFunction argument2
) {
double d = argument1.minValue();
double e = argument2.minValue();
double f = argument1.maxValue();
double g = argument2.maxValue();
if (type == net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.MIN
|| type == net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.MAX) {
boolean bl = d >= g;
boolean bl2 = e >= f;
if (bl || bl2) {
LOGGER.warn("Creating a " + type + " function between two non-overlapping inputs: " + argument1 + " and " + argument2);
}
}
double h = switch (type.ordinal()) {
case 0 -> d + e;
case 1 -> d > 0.0 && e > 0.0 ? d * e : (f < 0.0 && g < 0.0 ? f * g : Math.min(d * g, f * e));
case 2 -> Math.min(d, e);
case 3 -> Math.max(d, e);
default -> throw new MatchException(null, null);
};
double i = switch (type.ordinal()) {
case 0 -> f + g;
case 1 -> d > 0.0 && e > 0.0 ? f * g : (f < 0.0 && g < 0.0 ? d * e : Math.max(d * e, f * g));
case 2 -> Math.min(f, g);
case 3 -> Math.max(f, g);
default -> throw new MatchException(null, null);
};
if (type == net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.MUL
|| type == net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.ADD) {
if (argument1 instanceof DensityFunctions.Constant constant) {
return new DensityFunctions.MulOrAdd(
type == net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.ADD
? net.minecraft.world.level.levelgen.DensityFunctions.MulOrAdd.Type.ADD
: net.minecraft.world.level.levelgen.DensityFunctions.MulOrAdd.Type.MUL,
argument2,
h,
i,
constant.value
);
}
if (argument2 instanceof DensityFunctions.Constant constant) {
return new DensityFunctions.MulOrAdd(
type == net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.ADD
? net.minecraft.world.level.levelgen.DensityFunctions.MulOrAdd.Type.ADD
: net.minecraft.world.level.levelgen.DensityFunctions.MulOrAdd.Type.MUL,
argument1,
h,
i,
constant.value
);
}
}
return new DensityFunctions.Ap2(type, argument1, argument2, h, i);
}
net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type type();
DensityFunction argument1();
DensityFunction argument2();
@Override
default KeyDispatchDataCodec<? extends DensityFunction> codec() {
return this.type().codec;
}
}
protected record WeirdScaledSampler(DensityFunction input, DensityFunction.NoiseHolder noise, RarityValueMapper rarityValueMapper)
implements DensityFunctions.TransformerWithContext {
private static final MapCodec<DensityFunctions.WeirdScaledSampler> DATA_CODEC = RecordCodecBuilder.mapCodec(
instance -> instance.group(
DensityFunction.HOLDER_HELPER_CODEC.fieldOf("input").forGetter(DensityFunctions.WeirdScaledSampler::input),
DensityFunction.NoiseHolder.CODEC.fieldOf("noise").forGetter(DensityFunctions.WeirdScaledSampler::noise),
RarityValueMapper.CODEC.fieldOf("rarity_value_mapper").forGetter(DensityFunctions.WeirdScaledSampler::rarityValueMapper)
)
.apply(instance, DensityFunctions.WeirdScaledSampler::new)
);
public static final KeyDispatchDataCodec<DensityFunctions.WeirdScaledSampler> CODEC = DensityFunctions.makeCodec(DATA_CODEC);
@Override
public double transform(DensityFunction.FunctionContext context, double value) {
double d = this.rarityValueMapper.mapper.get(value);
return d * Math.abs(this.noise.getValue(context.blockX() / d, context.blockY() / d, context.blockZ() / d));
}
@Override
public DensityFunction mapAll(DensityFunction.Visitor visitor) {
return visitor.apply(new DensityFunctions.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<? extends DensityFunction> codec() {
return CODEC;
}
}
record YClampedGradient(int fromY, int toY, double fromValue, double toValue) implements DensityFunction.SimpleFunction {
private static final MapCodec<DensityFunctions.YClampedGradient> DATA_CODEC = RecordCodecBuilder.mapCodec(
instance -> instance.group(
Codec.intRange(DimensionType.MIN_Y * 2, DimensionType.MAX_Y * 2).fieldOf("from_y").forGetter(DensityFunctions.YClampedGradient::fromY),
Codec.intRange(DimensionType.MIN_Y * 2, DimensionType.MAX_Y * 2).fieldOf("to_y").forGetter(DensityFunctions.YClampedGradient::toY),
DensityFunctions.NOISE_VALUE_CODEC.fieldOf("from_value").forGetter(DensityFunctions.YClampedGradient::fromValue),
DensityFunctions.NOISE_VALUE_CODEC.fieldOf("to_value").forGetter(DensityFunctions.YClampedGradient::toValue)
)
.apply(instance, DensityFunctions.YClampedGradient::new)
);
public static final KeyDispatchDataCodec<DensityFunctions.YClampedGradient> 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<? extends DensityFunction> codec() {
return CODEC;
}
}
}