diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index e1008165..cc275fa7 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -2732,6 +2732,30 @@ public class Expression * map(range(10), str('%.1f',rand(_))) => [0.0, 0.4, 0.6, 1.9, 2.8, 3.8, 5.3, 2.2, 1.6, 5.6] * * + *

perlin(x), perlin(x,y), perlin(x,y,z), perlin(x,y,z,seed)

+ *

returns a noise value from 0.0 to 1.0 (roughly) for 1, 2 or 3 dimensional coordinate. The default seed it samples from is 0, but seed can be + * specified as a 4th argument as well. In case you need 1D or 2D noise values with custom seed, use null + * for z, or y and z arguments respectively.

+ *

Perlin noise is based on a square grid and generates rougher maps comparing to Simplex, which is creamier, + * but the in-game implementation allows to sample up to 3D noise. Querying for lower-dimensional result, rather than + * affixing unused dimensions to constants has a speed benefit, + *

+ *

Thou shall not sample from noise changing seed frequently. Scarpet will keep track of the last 256 perlin seeds used for sampling + * providing similar speed comparing to the default seed of 0. In case the app engine uses more than 256 seeds at the same time, + * switching between them can get much more expensive.

+ * + *

simplex(x,y), simplex(x,y,seed)

+ *

returns a noise value from 0.0 to 1.0 (roughly) for 2 dimensional coordinate. + * The default seed it samples from is 0, but seed can be + * specified as a 3rd argument as well

+ *

Simplex noise is based on a triangular grid and generates smoother maps comparing to Perlin, + * but the in-game implementation only allows to sample 2D noise. To sample 1D simplex noise, affix other coordinate + * to a constant. + *

+ *

Thou shall not sample from noise changing seed frequently. Scarpet will keep track of the last 256 simplex seeds used for sampling + * providing similar speed comparing to the default seed of 0. In case the app engine uses more than 256 seeds at the same time, + * switching between them can get much more expensive.

+ * *

print(expr)

*

prints the value of the expression to chat. * Passes the result of the argument to the output unchanged, so print-statements can @@ -2975,46 +2999,60 @@ public class Expression return (cc, tt) -> retval; }); - addLazyFunction("perlin", 3, (c, t, lv) -> { - // add seed cache - Value ret; - Value x = lv.get(0).evalValue(c); - Value y = lv.get(1).evalValue(c); - Value z = lv.get(2).evalValue(c); + addLazyFunction("perlin", -1, (c, t, lv) -> { + PerlinNoiseSampler sampler; + Value x, y, z; - if (z instanceof NullValue) + if (lv.size() >= 4) { - if (y instanceof NullValue) - { - ret = new NumericValue(PerlinNoiseSampler.instance.sample1d( - NumericValue.asNumber(x).getDouble() - )); - } - else - { - ret = new NumericValue(PerlinNoiseSampler.instance.sample2d( - NumericValue.asNumber(x).getDouble(), - NumericValue.asNumber(y).getDouble() - )); - } + x = lv.get(0).evalValue(c); + y = lv.get(1).evalValue(c); + z = lv.get(2).evalValue(c); + sampler = PerlinNoiseSampler.getPerlin(NumericValue.asNumber(lv.get(3).evalValue(c)).getLong()); } else { - ret = new NumericValue(PerlinNoiseSampler.instance.sample3d( + sampler = PerlinNoiseSampler.instance; + y = Value.NULL; + z = Value.NULL; + if (lv.size() == 0 ) + throw new InternalExpressionException("'perlin' requires at least one dimension to sample from"); + x = NumericValue.asNumber(lv.get(0).evalValue(c)); + if (lv.size() > 1) + { + y = NumericValue.asNumber(lv.get(1).evalValue(c)); + if (lv.size() > 2) + z = NumericValue.asNumber(lv.get(2).evalValue(c)); + } + } + + double result; + + if (z instanceof NullValue) + if (y instanceof NullValue) + result = sampler.sample1d(NumericValue.asNumber(x).getDouble()); + else + result = sampler.sample2d(NumericValue.asNumber(x).getDouble(), NumericValue.asNumber(y).getDouble()); + else + result = sampler.sample3d( NumericValue.asNumber(x).getDouble(), NumericValue.asNumber(y).getDouble(), - NumericValue.asNumber(z).getDouble() - )); - } + NumericValue.asNumber(z).getDouble()); + Value ret = new NumericValue(result); return (cc, tt) -> ret; }); - addLazyFunction("simplex", 2, (c, t, lv) -> { - // add seed cache + addLazyFunction("simplex", -1, (c, t, lv) -> { + SimplexNoiseSampler sampler; + if (lv.size() == 2) + sampler = SimplexNoiseSampler.instance; + else if (lv.size() == 3) + sampler = SimplexNoiseSampler.getSimplex(NumericValue.asNumber(lv.get(2).evalValue(c)).getLong()); + else + throw new InternalExpressionException("'simplex' requires two or three arguments"); double x = NumericValue.asNumber(lv.get(0).evalValue(c)).getDouble(); double y = NumericValue.asNumber(lv.get(1).evalValue(c)).getDouble(); - //double z = NumericValue.asNumber(lv.get(2).evalValue(c)).getDouble(); - Value value = new NumericValue(SimplexNoiseSampler.instance.sample(x,y)); + Value value = new NumericValue(sampler.sample(x,y)); return (cc, tt) -> value; }); diff --git a/src/main/java/carpet/script/utils/PerlinNoiseSampler.java b/src/main/java/carpet/script/utils/PerlinNoiseSampler.java index 300df75c..c57973d9 100644 --- a/src/main/java/carpet/script/utils/PerlinNoiseSampler.java +++ b/src/main/java/carpet/script/utils/PerlinNoiseSampler.java @@ -1,7 +1,11 @@ package carpet.script.utils; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; + +import java.util.Map; import java.util.Random; +// extracted from import net.minecraft.util.math.noise.PerlinNoiseSampler public class PerlinNoiseSampler { protected static final int[][] gradients3d = new int[][]{ {1, 1, 0}, {-1, 1, 0}, {1, -1, 0}, {-1, -1, 0}, @@ -17,6 +21,14 @@ public class PerlinNoiseSampler { public final double originY; public final double originZ; public static PerlinNoiseSampler instance = new PerlinNoiseSampler(new Random(0)); + public static Map samplers = new Long2ObjectOpenHashMap<>(); + + public static PerlinNoiseSampler getPerlin(long aLong) + { + if (samplers.size() > 256) + samplers.clear(); + return samplers.computeIfAbsent(aLong, seed -> new PerlinNoiseSampler(new Random(seed))); + } public PerlinNoiseSampler(Random random) { this.originX = random.nextDouble() * 256.0D; @@ -38,6 +50,8 @@ public class PerlinNoiseSampler { } + + //3D public double sample3d(double x, double y, double z) {//, double d, double e) { double f = x + this.originX; @@ -61,7 +75,7 @@ public class PerlinNoiseSampler { t = 0.0D; }*/ //return this.sample(i, j, k, l, m - t, n, o, p, q); - return this.sample3d(i, j, k, l, m, n, o, p, q); + return this.sample3d(i, j, k, l, m, n, o, p, q)/2+0.5; } private double sample3d(int sectionX, int sectionY, int sectionZ, double localX, double localY, double localZ, double fadeLocalX, double fadeLocalY, double fadeLocalZ) { @@ -105,7 +119,7 @@ public class PerlinNoiseSampler { double m = g - (double)j; double o = perlinFade(l); double p = perlinFade(m); - return this.sample2d(i, j, l, m, o, p); + return this.sample2d(i, j, l, m, o, p)/2+0.5; } private double sample2d(int sectionX, int sectionY, double localX, double localY, double fadeLocalX, double fadeLocalY) { @@ -138,7 +152,7 @@ public class PerlinNoiseSampler { int i = floor(f); double l = f - (double)i; double o = perlinFade(l); - return this.sample1d(i, l, o); + return this.sample1d(i, l, o)+0.5; } private double sample1d(int sectionX, double localX, double fadeLocalX) { double d = grad1d(this.getGradient(sectionX), localX); diff --git a/src/main/java/carpet/script/utils/SimplexNoiseSampler.java b/src/main/java/carpet/script/utils/SimplexNoiseSampler.java index 06c5b71b..7715d915 100644 --- a/src/main/java/carpet/script/utils/SimplexNoiseSampler.java +++ b/src/main/java/carpet/script/utils/SimplexNoiseSampler.java @@ -1,13 +1,25 @@ package carpet.script.utils; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; + +import java.util.Map; import java.util.Random; +// extracted from import net.minecraft.util.math.noise.SimplexNoiseSampler public class SimplexNoiseSampler extends PerlinNoiseSampler { private static final double sqrt3 = Math.sqrt(3.0D); private static final double SKEW_FACTOR_2D; private static final double UNSKEW_FACTOR_2D; public static SimplexNoiseSampler instance = new SimplexNoiseSampler(new Random(0)); + public static Map samplers = new Long2ObjectOpenHashMap<>(); + + public static SimplexNoiseSampler getSimplex(long aLong) + { + if (samplers.size() > 256) + samplers.clear(); + return samplers.computeIfAbsent(aLong, seed -> new SimplexNoiseSampler(new Random(seed))); + } public SimplexNoiseSampler(Random random) { super(random); @@ -57,7 +69,8 @@ public class SimplexNoiseSampler extends PerlinNoiseSampler { double aa = this.grad(v, h, k, 0.0D, 0.5D); double ab = this.grad(w, p, q, 0.0D, 0.5D); double ac = this.grad(z, r, s, 0.0D, 0.5D); - return 70.0D * (aa + ab + ac); + //return 70.0D * (aa + ab + ac); + return 35.0D * (aa + ab + ac)+0.5; } static {