diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index cc275fa7..d5ad5457 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -2732,25 +2732,26 @@ 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
+ *
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 + *
Perlin noise is based on a square grid and generates rougher maps comparing to Simplex, which is creamier. + * 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.
+ *
simplex(x, y), simplex(x, y, z), simplex(x, y, z, seed)returns a noise value from 0.0 to 1.0 (roughly) for 2 or 3 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.
+ * specified as a 4th argument as well. In case you need 2D noise values with custom seed, use null
+ * for z argument.
Simplex noise is based on a triangular grid and generates smoother maps comparing to Perlin. + * 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,
@@ -3044,16 +3045,37 @@ public class Expression
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());
+ Value x, y, z;
+
+ if (lv.size() >= 4)
+ {
+ x = lv.get(0).evalValue(c);
+ y = lv.get(1).evalValue(c);
+ z = lv.get(2).evalValue(c);
+ sampler = SimplexNoiseSampler.getSimplex(NumericValue.asNumber(lv.get(3).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();
- Value value = new NumericValue(sampler.sample(x,y));
- return (cc, tt) -> value;
+ {
+ sampler = SimplexNoiseSampler.instance;
+ z = Value.NULL;
+ if (lv.size() < 2 )
+ throw new InternalExpressionException("'simplex' requires at least two dimensions to sample from");
+ x = NumericValue.asNumber(lv.get(0).evalValue(c));
+ 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)
+ 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());
+ Value ret = new NumericValue(result);
+ return (cc, tt) -> ret;
});
addUnaryFunction("print", (v) ->
diff --git a/src/main/java/carpet/script/utils/SimplexNoiseSampler.java b/src/main/java/carpet/script/utils/SimplexNoiseSampler.java
index 7715d915..c2083028 100644
--- a/src/main/java/carpet/script/utils/SimplexNoiseSampler.java
+++ b/src/main/java/carpet/script/utils/SimplexNoiseSampler.java
@@ -38,7 +38,7 @@ public class SimplexNoiseSampler extends PerlinNoiseSampler {
return g;
}
- public double sample(double x, double y) {
+ public double sample2d(double x, double y) {
double d = (x + y) * SKEW_FACTOR_2D;
int i = PerlinNoiseSampler.floor(x + d);
int j = PerlinNoiseSampler.floor(y + d);
@@ -73,6 +73,95 @@ public class SimplexNoiseSampler extends PerlinNoiseSampler {
return 35.0D * (aa + ab + ac)+0.5;
}
+ public double sample3d(double d, double e, double f) {
+ double g = 0.3333333333333333D;
+ double h = (d + e + f) * 0.3333333333333333D;
+ int i = floor(d + h);
+ int j = floor(e + h);
+ int k = floor(f + h);
+ double l = 0.16666666666666666D;
+ double m = (double)(i + j + k) * 0.16666666666666666D;
+ double n = (double)i - m;
+ double o = (double)j - m;
+ double p = (double)k - m;
+ double q = d - n;
+ double r = e - o;
+ double s = f - p;
+ byte z;
+ byte aa;
+ byte ab;
+ byte ac;
+ byte ad;
+ byte bc;
+ if (q >= r) {
+ if (r >= s) {
+ z = 1;
+ aa = 0;
+ ab = 0;
+ ac = 1;
+ ad = 1;
+ bc = 0;
+ } else if (q >= s) {
+ z = 1;
+ aa = 0;
+ ab = 0;
+ ac = 1;
+ ad = 0;
+ bc = 1;
+ } else {
+ z = 0;
+ aa = 0;
+ ab = 1;
+ ac = 1;
+ ad = 0;
+ bc = 1;
+ }
+ } else if (r < s) {
+ z = 0;
+ aa = 0;
+ ab = 1;
+ ac = 0;
+ ad = 1;
+ bc = 1;
+ } else if (q < s) {
+ z = 0;
+ aa = 1;
+ ab = 0;
+ ac = 0;
+ ad = 1;
+ bc = 1;
+ } else {
+ z = 0;
+ aa = 1;
+ ab = 0;
+ ac = 1;
+ ad = 1;
+ bc = 0;
+ }
+
+ double bd = q - (double)z + 0.16666666666666666D;
+ double be = r - (double)aa + 0.16666666666666666D;
+ double bf = s - (double)ab + 0.16666666666666666D;
+ double bg = q - (double)ac + 0.3333333333333333D;
+ double bh = r - (double)ad + 0.3333333333333333D;
+ double bi = s - (double)bc + 0.3333333333333333D;
+ double bj = q - 1.0D + 0.5D;
+ double bk = r - 1.0D + 0.5D;
+ double bl = s - 1.0D + 0.5D;
+ int bm = i & 255;
+ int bn = j & 255;
+ int bo = k & 255;
+ int bp = this.getGradient(bm + this.getGradient(bn + this.getGradient(bo))) % 12;
+ int bq = this.getGradient(bm + z + this.getGradient(bn + aa + this.getGradient(bo + ab))) % 12;
+ int br = this.getGradient(bm + ac + this.getGradient(bn + ad + this.getGradient(bo + bc))) % 12;
+ int bs = this.getGradient(bm + 1 + this.getGradient(bn + 1 + this.getGradient(bo + 1))) % 12;
+ double bt = this.grad(bp, q, r, s, 0.6D);
+ double bu = this.grad(bq, bd, be, bf, 0.6D);
+ double bv = this.grad(br, bg, bh, bi, 0.6D);
+ double bw = this.grad(bs, bj, bk, bl, 0.6D);
+ return 16.0D * (bt + bu + bv + bw) + 0.5;
+ }
+
static {
SKEW_FACTOR_2D = 0.5D * (sqrt3 - 1.0D);
UNSKEW_FACTOR_2D = (3.0D - sqrt3) / 6.0D;