diff --git a/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/noise/GaborNoiseSampler.java b/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/noise/GaborNoiseSampler.java new file mode 100644 index 000000000..93f006dc4 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/noise/GaborNoiseSampler.java @@ -0,0 +1,105 @@ +package com.dfsek.terra.api.math.noise.samplers.noise; + +import com.dfsek.terra.api.math.noise.samplers.noise.random.WhiteNoiseSampler; +import net.jafama.FastMath; + +public class GaborNoiseSampler extends NoiseFunction { + private final WhiteNoiseSampler rand; + private double k = 1.0; + private double a = 0.1; + private double f0 = 0.625; + private double kernelRadius = (FastMath.sqrt(-FastMath.log(0.05) / Math.PI) / a); + private double omega0 = Math.PI * 0.25; + private boolean isotropic = true; + private double impulsesPerKernel = 64d; + private double impulseDensity = (impulsesPerKernel / (Math.PI * kernelRadius * kernelRadius)); + + private double impulsesPerCell = impulseDensity * kernelRadius * kernelRadius; + private double g = FastMath.exp(-impulsesPerCell); + + + public GaborNoiseSampler(int seed) { + super(seed); + rand = new WhiteNoiseSampler(seed); + } + + public void setIsotropic(boolean isotropic) { + this.isotropic = isotropic; + } + + public void setImpulsesPerKernel(double impulsesPerKernel) { + this.impulsesPerKernel = impulsesPerKernel; + recalculateRadiusAndDensity(); + } + + public void setA(double a) { + this.a = a; + recalculateRadiusAndDensity(); + } + + public void setFrequency0(double f0) { + this.f0 = f0; + } + + public void setRotation(double omega0) { + this.omega0 = Math.PI * omega0; + } + + public void setDeviation(double k) { + this.k = k; + } + + private void recalculateRadiusAndDensity() { + kernelRadius = (FastMath.sqrt(-FastMath.log(0.05) / Math.PI) / this.a); + impulseDensity = (impulsesPerKernel / (Math.PI * kernelRadius * kernelRadius)); + impulsesPerCell = impulseDensity * kernelRadius * kernelRadius; + g = FastMath.exp(-impulsesPerCell); + } + + @Override + public double getNoiseRaw(int seed, double x, double z) { + return gaborNoise(seed, x, z); + } + + @Override + public double getNoiseRaw(int seed, double x, double y, double z) { + return gaborNoise(seed, x, z); + } + + private double gaborNoise(int seed, double x, double y) { + x /= kernelRadius; + y /= kernelRadius; + int xi = fastFloor(x); + int yi = fastFloor(y); + double xf = x - xi; + double yf = y - yi; + double noise = 0; + for(int dx = -1; dx <= 1; dx++) { + for(int dz = -1; dz <= 1; dz++) { + noise += calculateCell(seed, xi + dx, yi + dz, xf - dx, yf - dz); + } + } + return noise; + } + + private double calculateCell(int seed, int xi, int yi, double x, double y) { + long mashedSeed = murmur64(31L * xi + yi) + seed; + + double gaussianSource = (rand.getNoiseRaw(mashedSeed++) + 1) / 2; + int impulses = 0; + while(gaussianSource > g) { + impulses++; + gaussianSource *= (rand.getNoiseRaw(mashedSeed++) + 1) / 2; + } + + double noise = 0; + for(int i = 0; i < impulses; i++) { + noise += rand.getNoiseRaw(mashedSeed++) * gabor(isotropic ? (rand.getNoiseRaw(mashedSeed++) + 1) * Math.PI : omega0, x * kernelRadius, y * kernelRadius); + } + return noise; + } + + private double gabor(double omega_0, double x, double y) { + return k * (FastMath.exp(-Math.PI * (a * a) * (x * x + y * y)) * fastCos(2 * Math.PI * f0 * (x * fastCos(omega_0) + y * fastSin(omega_0)))); + } +} diff --git a/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/noise/NoiseFunction.java b/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/noise/NoiseFunction.java index 6c5829f44..1266c3762 100644 --- a/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/noise/NoiseFunction.java +++ b/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/noise/NoiseFunction.java @@ -22,6 +22,8 @@ public abstract class NoiseFunction implements NoiseSampler { return f >= 0 ? (int) f : (int) f - 1; } + static final int precision = 100; + protected static int hash(int seed, int xPrimed, int yPrimed, int zPrimed) { int hash = seed ^ xPrimed ^ yPrimed ^ zPrimed; @@ -36,6 +38,8 @@ public abstract class NoiseFunction implements NoiseSampler { return hash; } + static final int modulus = 360 * precision; + protected static int fastRound(double f) { return f >= 0 ? (int) (f + 0.5f) : (int) (f - 0.5); } @@ -73,6 +77,48 @@ public abstract class NoiseFunction implements NoiseSampler { return FastMath.sqrt(f); } + static final double[] sin = new double[360 * 100]; // lookup table + + static { + for(int i = 0; i < sin.length; i++) { + sin[i] = (float) Math.sin((double) (i) / (precision)); + } + } + + protected static int fastCeil(double f) { + int i = (int) f; + if(i < f) i++; + return i; + } + + /** + * Murmur64 hashing function + * + * @param h Input value + * @return Hashed value + */ + protected static long murmur64(long h) { + h ^= h >>> 33; + h *= 0xff51afd7ed558ccdL; + h ^= h >>> 33; + h *= 0xc4ceb9fe1a85ec53L; + h ^= h >>> 33; + return h; + } + + private static double sinLookup(int a) { + return a >= 0 ? sin[a % (modulus)] : -sin[-a % (modulus)]; + } + + protected static double fastSin(double a) { + return sinLookup((int) (a * precision + 0.5f)); + } + + protected static double fastCos(double a) { + return sinLookup((int) ((a + Math.PI / 2) * precision + 0.5f)); + } + + public void setSeed(int seed) { this.seed = seed; } diff --git a/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/noise/random/WhiteNoiseSampler.java b/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/noise/random/WhiteNoiseSampler.java index 80660949f..962ca2486 100644 --- a/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/noise/random/WhiteNoiseSampler.java +++ b/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/noise/random/WhiteNoiseSampler.java @@ -12,38 +12,41 @@ public class WhiteNoiseSampler extends NoiseFunction { super(seed); } + public double getNoiseRaw(long seed) { + return (Double.longBitsToDouble((murmur64(seed) & 0x000fffffffffffffL) | POSITIVE_POW1) - 1.5) * 2; + } + @Override public double getNoiseRaw(int seed, double x, double y) { - long hashX = Double.doubleToRawLongBits(x) ^ seed; - long hashZ = Double.doubleToRawLongBits(y) ^ seed; - long hash = ((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed; - long base = (murmur64(hash) & 0x000fffffffffffffL) - | POSITIVE_POW1; // Sign and exponent - return (Double.longBitsToDouble(base) - 1.5) * 2; + return (getNoiseUnmapped(seed, x, y) - 1.5) * 2; } @Override public double getNoiseRaw(int seed, double x, double y, double z) { + return (getNoiseUnmapped(seed, x, y, z) - 1.5) * 2; + } + + public double getNoiseUnmapped(int seed, double x, double y, double z) { + long base = ((randomBits(seed, x, y, z)) & 0x000fffffffffffffL) | POSITIVE_POW1; // Sign and exponent + return Double.longBitsToDouble(base); + } + + public double getNoiseUnmapped(int seed, double x, double y) { + long base = (randomBits(seed, x, y) & 0x000fffffffffffffL) | POSITIVE_POW1; // Sign and exponent + return Double.longBitsToDouble(base); + } + + public long randomBits(int seed, double x, double y, double z) { long hashX = Double.doubleToRawLongBits(x) ^ seed; long hashZ = Double.doubleToRawLongBits(y) ^ seed; long hash = (((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed) + Double.doubleToRawLongBits(z); - long base = ((murmur64(hash)) & 0x000fffffffffffffL) - | POSITIVE_POW1; // Sign and exponent - return (Double.longBitsToDouble(base) - 1.5) * 2; + return murmur64(hash); } - /** - * Murmur64 hashing function - * - * @param h Input value - * @return Hashed value - */ - private static long murmur64(long h) { - h ^= h >>> 33; - h *= 0xff51afd7ed558ccdL; - h ^= h >>> 33; - h *= 0xc4ceb9fe1a85ec53L; - h ^= h >>> 33; - return h; + public long randomBits(int seed, double x, double y) { + long hashX = Double.doubleToRawLongBits(x) ^ seed; + long hashZ = Double.doubleToRawLongBits(y) ^ seed; + long hash = ((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed; + return murmur64(hash); } } diff --git a/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/noise/simplex/SimplexSampler.java b/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/noise/simplex/SimplexSampler.java new file mode 100644 index 000000000..b12a75d21 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/noise/simplex/SimplexSampler.java @@ -0,0 +1,243 @@ +package com.dfsek.terra.api.math.noise.samplers.noise.simplex; + +public class SimplexSampler extends SimplexStyleSampler { + private static final Double2[] GRAD_2D = { + new Double2(-1, -1), new Double2(1, -1), new Double2(-1, 1), new Double2(1, 1), + new Double2(0, -1), new Double2(-1, 0), new Double2(0, 1), new Double2(1, 0), + }; + private static final Double3[] GRAD_3D = { + new Double3(1, 1, 0), new Double3(-1, 1, 0), new Double3(1, -1, 0), new Double3(-1, -1, 0), + new Double3(1, 0, 1), new Double3(-1, 0, 1), new Double3(1, 0, -1), new Double3(-1, 0, -1), + new Double3(0, 1, 1), new Double3(0, -1, 1), new Double3(0, 1, -1), new Double3(0, -1, -1), + new Double3(1, 1, 0), new Double3(0, -1, 1), new Double3(-1, 1, 0), new Double3(0, -1, -1), + }; + + private static final double F2 = 1.0 / 2.0; + private static final double F3 = (1.0 / 3.0); + private static final double G2 = 1.0 / 4.0; + private static final double G3 = (1.0 / 6.0); + private static final double G33 = G3 * 3 - 1; + + private static final int X_PRIME = 1619; + private static final int Y_PRIME = 31337; + private static final int Z_PRIME = 6971; + + + public SimplexSampler(int seed) { + super(seed); + } + + private static double gradCoord3D(int seed, int x, int y, int z, double xd, double yd, double zd) { + int hash = seed; + hash ^= X_PRIME * x; + hash ^= Y_PRIME * y; + hash ^= Z_PRIME * z; + + hash = hash * hash * hash * 60493; + hash = (hash >> 13) ^ hash; + + Double3 g = GRAD_3D[hash & 15]; + + return xd * g.x + yd * g.y + zd * g.z; + } + + private static double gradCoord2D(int seed, int x, int y, double xd, double yd) { + int hash = seed; + hash ^= X_PRIME * x; + hash ^= Y_PRIME * y; + + hash = hash * hash * hash * 60493; + hash = (hash >> 13) ^ hash; + + Double2 g = GRAD_2D[hash & 7]; + + return xd * g.x + yd * g.y; + } + + @Override + public double getNoiseRaw(int seed, double x, double y) { + double t = (x + y) * F2; + int i = fastFloor(x + t); + int j = fastFloor(y + t); + + t = (i + j) * G2; + double X0 = i - t; + double Y0 = j - t; + + double x0 = x - X0; + double y0 = y - Y0; + + int i1, j1; + if(x0 > y0) { + i1 = 1; + j1 = 0; + } else { + i1 = 0; + j1 = 1; + } + + double x1 = x0 - i1 + G2; + double y1 = y0 - j1 + G2; + double x2 = x0 - 1 + F2; + double y2 = y0 - 1 + F2; + + double n0, n1, n2; + + t = 0.5 - x0 * x0 - y0 * y0; + if(t < 0) { + n0 = 0; + } else { + t *= t; + n0 = t * t * gradCoord2D(seed, i, j, x0, y0); + } + + t = 0.5 - x1 * x1 - y1 * y1; + if(t < 0) { + n1 = 0; + } else { + t *= t; + n1 = t * t * gradCoord2D(seed, i + i1, j + j1, x1, y1); + } + + t = 0.5 - x2 * x2 - y2 * y2; + if(t < 0) { + n2 = 0; + } else { + t *= t; + n2 = t * t * gradCoord2D(seed, i + 1, j + 1, x2, y2); + } + + return 50 * (n0 + n1 + n2); + } + + @Override + public double getNoiseRaw(int seed, double x, double y, double z) { + double t = (x + y + z) * F3; + int i = fastFloor(x + t); + int j = fastFloor(y + t); + int k = fastFloor(z + t); + + t = (i + j + k) * G3; + double x0 = x - (i - t); + double y0 = y - (j - t); + double z0 = z - (k - t); + + int i1, j1, k1; + int i2, j2, k2; + + if(x0 >= y0) { + if(y0 >= z0) { + i1 = 1; + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 1; + k2 = 0; + } else if(x0 >= z0) { + i1 = 1; + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 0; + k2 = 1; + } else // x0 < z0 + { + i1 = 0; + j1 = 0; + k1 = 1; + i2 = 1; + j2 = 0; + k2 = 1; + } + } else // x0 < y0 + { + if(y0 < z0) { + i1 = 0; + j1 = 0; + k1 = 1; + i2 = 0; + j2 = 1; + k2 = 1; + } else if(x0 < z0) { + i1 = 0; + j1 = 1; + k1 = 0; + i2 = 0; + j2 = 1; + k2 = 1; + } else // x0 >= z0 + { + i1 = 0; + j1 = 1; + k1 = 0; + i2 = 1; + j2 = 1; + k2 = 0; + } + } + + double x1 = x0 - i1 + G3; + double y1 = y0 - j1 + G3; + double z1 = z0 - k1 + G3; + double x2 = x0 - i2 + F3; + double y2 = y0 - j2 + F3; + double z2 = z0 - k2 + F3; + double x3 = x0 + G33; + double y3 = y0 + G33; + double z3 = z0 + G33; + + double n0, n1, n2, n3; + + t = 0.6 - x0 * x0 - y0 * y0 - z0 * z0; + if(t < 0) n0 = 0; + else { + t *= t; + n0 = t * t * gradCoord3D(seed, i, j, k, x0, y0, z0); + } + + t = 0.6 - x1 * x1 - y1 * y1 - z1 * z1; + if(t < 0) { + n1 = 0; + } else { + t *= t; + n1 = t * t * gradCoord3D(seed, i + i1, j + j1, k + k1, x1, y1, z1); + } + + t = 0.6 - x2 * x2 - y2 * y2 - z2 * z2; + if(t < 0) { + n2 = 0; + } else { + t *= t; + n2 = t * t * gradCoord3D(seed, i + i2, j + j2, k + k2, x2, y2, z2); + } + + t = 0.6 - x3 * x3 - y3 * y3 - z3 * z3; + if(t < 0) { + n3 = 0; + } else { + t *= t; + n3 = t * t * gradCoord3D(seed, i + 1, j + 1, k + 1, x3, y3, z3); + } + + return 32 * (n0 + n1 + n2 + n3); + } + + private static class Double2 { + public final double x, y; + + public Double2(double x, double y) { + this.x = x; + this.y = y; + } + } + + private static class Double3 { + public final double x, y, z; + + public Double3(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + } +} diff --git a/common/src/main/java/com/dfsek/terra/config/GenericLoaders.java b/common/src/main/java/com/dfsek/terra/config/GenericLoaders.java index 6eafad8d9..0726b8d97 100644 --- a/common/src/main/java/com/dfsek/terra/config/GenericLoaders.java +++ b/common/src/main/java/com/dfsek/terra/config/GenericLoaders.java @@ -95,8 +95,6 @@ public class GenericLoaders implements LoaderRegistrar { .registerLoader(CarverPalette.class, new CarverPaletteLoader()) .registerLoader(SourceSeeded.class, new SourceBuilderLoader()) .registerLoader(StageSeeded.class, new StageBuilderLoader()) - .registerLoader(TerraAddon.class, main.getAddons()) - .registerLoader(BlockType.class, (t, object, cf) -> main.getWorldHandle().createBlockData((String) object).getBlockType()) .registerLoader(BiomeProvider.BiomeProviderBuilder.class, new BiomeProviderBuilderLoader()) .registerLoader(ImageSampler.Channel.class, (t, object, cf) -> ImageSampler.Channel.valueOf((String) object)) .registerLoader(BiomeProvider.Type.class, (t, object, cf) -> BiomeProvider.Type.valueOf((String) object)) @@ -107,5 +105,10 @@ public class GenericLoaders implements LoaderRegistrar { .registerLoader(CellularSampler.ReturnType.class, (t, object, cf) -> CellularSampler.ReturnType.valueOf((String) object)) .registerLoader(CellularSampler.DistanceFunction.class, (t, object, cf) -> CellularSampler.DistanceFunction.valueOf((String) object)) .registerLoader(TerraFlora.Search.class, (t, o, l) -> TerraFlora.Search.valueOf(o.toString())); + + if(main != null) { + registry.registerLoader(TerraAddon.class, main.getAddons()) + .registerLoader(BlockType.class, (t, object, cf) -> main.getWorldHandle().createBlockData((String) object).getBlockType()); + } } } diff --git a/common/src/main/java/com/dfsek/terra/config/loaders/config/sampler/templates/noise/CellularNoiseTemplate.java b/common/src/main/java/com/dfsek/terra/config/loaders/config/sampler/templates/noise/CellularNoiseTemplate.java index 3852742b9..90a53d277 100644 --- a/common/src/main/java/com/dfsek/terra/config/loaders/config/sampler/templates/noise/CellularNoiseTemplate.java +++ b/common/src/main/java/com/dfsek/terra/config/loaders/config/sampler/templates/noise/CellularNoiseTemplate.java @@ -39,7 +39,7 @@ public class CellularNoiseTemplate extends NoiseTemplate { @Override public NoiseSampler apply(Long seed) { - CellularSampler sampler = new CellularSampler((int) (long) seed); + CellularSampler sampler = new CellularSampler((int) (long) seed + salt); sampler.setNoiseLookup(lookup.apply(seed)); sampler.setFrequency(frequency); sampler.setJitterModifier(cellularJitter); diff --git a/common/src/main/java/com/dfsek/terra/config/loaders/config/sampler/templates/noise/GaborNoiseTemplate.java b/common/src/main/java/com/dfsek/terra/config/loaders/config/sampler/templates/noise/GaborNoiseTemplate.java new file mode 100644 index 000000000..b15aa75cd --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/config/loaders/config/sampler/templates/noise/GaborNoiseTemplate.java @@ -0,0 +1,40 @@ +package com.dfsek.terra.config.loaders.config.sampler.templates.noise; + +import com.dfsek.tectonic.annotations.Default; +import com.dfsek.tectonic.annotations.Value; +import com.dfsek.terra.api.math.noise.NoiseSampler; +import com.dfsek.terra.api.math.noise.samplers.noise.GaborNoiseSampler; + +public class GaborNoiseTemplate extends NoiseTemplate { + @Value("rotation") + @Default + private double rotation = 0.25; + + @Value("isotropic") + @Default + private boolean isotropic = true; + + @Value("deviation") + @Default + private double deviation = 1.0; + + @Value("impulses") + @Default + private double impulses = 64d; + + @Value("frequency_0") + @Default + private double f0 = 0.625; + + @Override + public NoiseSampler apply(Long seed) { + GaborNoiseSampler gaborNoiseSampler = new GaborNoiseSampler((int) (long) seed + salt); + gaborNoiseSampler.setFrequency(frequency); + gaborNoiseSampler.setRotation(rotation); + gaborNoiseSampler.setIsotropic(isotropic); + gaborNoiseSampler.setDeviation(deviation); + gaborNoiseSampler.setImpulsesPerKernel(impulses); + gaborNoiseSampler.setFrequency0(f0); + return gaborNoiseSampler; + } +} diff --git a/common/src/main/java/com/dfsek/terra/config/loaders/config/sampler/templates/noise/SimpleNoiseTemplate.java b/common/src/main/java/com/dfsek/terra/config/loaders/config/sampler/templates/noise/SimpleNoiseTemplate.java index 731096c0f..9c1ab1cb2 100644 --- a/common/src/main/java/com/dfsek/terra/config/loaders/config/sampler/templates/noise/SimpleNoiseTemplate.java +++ b/common/src/main/java/com/dfsek/terra/config/loaders/config/sampler/templates/noise/SimpleNoiseTemplate.java @@ -14,7 +14,7 @@ public class SimpleNoiseTemplate extends NoiseTemplate { @Override public NoiseSampler apply(Long seed) { - NoiseFunction sampler = samplerSupplier.apply((int) (long) seed); + NoiseFunction sampler = samplerSupplier.apply((int) (long) seed + salt); sampler.setFrequency(frequency); return sampler; } diff --git a/common/src/main/java/com/dfsek/terra/registry/config/NoiseRegistry.java b/common/src/main/java/com/dfsek/terra/registry/config/NoiseRegistry.java index d84227455..ccbfee9ca 100644 --- a/common/src/main/java/com/dfsek/terra/registry/config/NoiseRegistry.java +++ b/common/src/main/java/com/dfsek/terra/registry/config/NoiseRegistry.java @@ -6,6 +6,7 @@ import com.dfsek.terra.api.math.noise.samplers.noise.random.WhiteNoiseSampler; import com.dfsek.terra.api.math.noise.samplers.noise.simplex.OpenSimplex2SSampler; import com.dfsek.terra.api.math.noise.samplers.noise.simplex.OpenSimplex2Sampler; import com.dfsek.terra.api.math.noise.samplers.noise.simplex.PerlinSampler; +import com.dfsek.terra.api.math.noise.samplers.noise.simplex.SimplexSampler; import com.dfsek.terra.api.math.noise.samplers.noise.value.ValueCubicSampler; import com.dfsek.terra.api.math.noise.samplers.noise.value.ValueSampler; import com.dfsek.terra.api.util.seeded.NoiseSeeded; @@ -15,6 +16,7 @@ import com.dfsek.terra.config.loaders.config.sampler.templates.KernelTemplate; import com.dfsek.terra.config.loaders.config.sampler.templates.noise.CellularNoiseTemplate; import com.dfsek.terra.config.loaders.config.sampler.templates.noise.ConstantNoiseTemplate; import com.dfsek.terra.config.loaders.config.sampler.templates.noise.ExpressionFunctionTemplate; +import com.dfsek.terra.config.loaders.config.sampler.templates.noise.GaborNoiseTemplate; import com.dfsek.terra.config.loaders.config.sampler.templates.noise.SimpleNoiseTemplate; import com.dfsek.terra.config.loaders.config.sampler.templates.noise.fractal.BrownianMotionTemplate; import com.dfsek.terra.config.loaders.config.sampler.templates.noise.fractal.PingPongTemplate; @@ -44,6 +46,8 @@ public class NoiseRegistry extends OpenRegistry new SimpleNoiseTemplate(OpenSimplex2Sampler::new)); add("OPENSIMPLEX2S", () -> new SimpleNoiseTemplate(OpenSimplex2SSampler::new)); add("PERLIN", () -> new SimpleNoiseTemplate(PerlinSampler::new)); + add("SIMPLEX", () -> new SimpleNoiseTemplate(SimplexSampler::new)); + add("GABOR", GaborNoiseTemplate::new); add("VALUE", () -> new SimpleNoiseTemplate(ValueSampler::new));