add Gabor noise

This commit is contained in:
dfsek 2021-03-11 19:03:57 -07:00
parent 9a6c34a2d5
commit 074ad44bca
9 changed files with 470 additions and 26 deletions

View File

@ -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))));
}
}

View File

@ -22,6 +22,8 @@ public abstract class NoiseFunction implements NoiseSampler {
return f >= 0 ? (int) f : (int) f - 1; 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) { protected static int hash(int seed, int xPrimed, int yPrimed, int zPrimed) {
int hash = seed ^ xPrimed ^ yPrimed ^ zPrimed; int hash = seed ^ xPrimed ^ yPrimed ^ zPrimed;
@ -36,6 +38,8 @@ public abstract class NoiseFunction implements NoiseSampler {
return hash; return hash;
} }
static final int modulus = 360 * precision;
protected static int fastRound(double f) { protected static int fastRound(double f) {
return f >= 0 ? (int) (f + 0.5f) : (int) (f - 0.5); 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); 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) { public void setSeed(int seed) {
this.seed = seed; this.seed = seed;
} }

View File

@ -12,38 +12,41 @@ public class WhiteNoiseSampler extends NoiseFunction {
super(seed); super(seed);
} }
public double getNoiseRaw(long seed) {
return (Double.longBitsToDouble((murmur64(seed) & 0x000fffffffffffffL) | POSITIVE_POW1) - 1.5) * 2;
}
@Override @Override
public double getNoiseRaw(int seed, double x, double y) { public double getNoiseRaw(int seed, double x, double y) {
long hashX = Double.doubleToRawLongBits(x) ^ seed; return (getNoiseUnmapped(seed, x, y) - 1.5) * 2;
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;
} }
@Override @Override
public double getNoiseRaw(int seed, double x, double y, double z) { 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 hashX = Double.doubleToRawLongBits(x) ^ seed;
long hashZ = Double.doubleToRawLongBits(y) ^ seed; long hashZ = Double.doubleToRawLongBits(y) ^ seed;
long hash = (((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed) + Double.doubleToRawLongBits(z); long hash = (((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed) + Double.doubleToRawLongBits(z);
long base = ((murmur64(hash)) & 0x000fffffffffffffL) return murmur64(hash);
| POSITIVE_POW1; // Sign and exponent
return (Double.longBitsToDouble(base) - 1.5) * 2;
} }
/** public long randomBits(int seed, double x, double y) {
* Murmur64 hashing function long hashX = Double.doubleToRawLongBits(x) ^ seed;
* long hashZ = Double.doubleToRawLongBits(y) ^ seed;
* @param h Input value long hash = ((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed;
* @return Hashed value return murmur64(hash);
*/
private static long murmur64(long h) {
h ^= h >>> 33;
h *= 0xff51afd7ed558ccdL;
h ^= h >>> 33;
h *= 0xc4ceb9fe1a85ec53L;
h ^= h >>> 33;
return h;
} }
} }

View File

@ -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;
}
}
}

View File

@ -95,8 +95,6 @@ public class GenericLoaders implements LoaderRegistrar {
.registerLoader(CarverPalette.class, new CarverPaletteLoader()) .registerLoader(CarverPalette.class, new CarverPaletteLoader())
.registerLoader(SourceSeeded.class, new SourceBuilderLoader()) .registerLoader(SourceSeeded.class, new SourceBuilderLoader())
.registerLoader(StageSeeded.class, new StageBuilderLoader()) .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(BiomeProvider.BiomeProviderBuilder.class, new BiomeProviderBuilderLoader())
.registerLoader(ImageSampler.Channel.class, (t, object, cf) -> ImageSampler.Channel.valueOf((String) object)) .registerLoader(ImageSampler.Channel.class, (t, object, cf) -> ImageSampler.Channel.valueOf((String) object))
.registerLoader(BiomeProvider.Type.class, (t, object, cf) -> BiomeProvider.Type.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.ReturnType.class, (t, object, cf) -> CellularSampler.ReturnType.valueOf((String) object))
.registerLoader(CellularSampler.DistanceFunction.class, (t, object, cf) -> CellularSampler.DistanceFunction.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())); .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());
}
} }
} }

View File

@ -39,7 +39,7 @@ public class CellularNoiseTemplate extends NoiseTemplate<CellularSampler> {
@Override @Override
public NoiseSampler apply(Long seed) { 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.setNoiseLookup(lookup.apply(seed));
sampler.setFrequency(frequency); sampler.setFrequency(frequency);
sampler.setJitterModifier(cellularJitter); sampler.setJitterModifier(cellularJitter);

View File

@ -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<GaborNoiseSampler> {
@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;
}
}

View File

@ -14,7 +14,7 @@ public class SimpleNoiseTemplate extends NoiseTemplate<NoiseFunction> {
@Override @Override
public NoiseSampler apply(Long seed) { public NoiseSampler apply(Long seed) {
NoiseFunction sampler = samplerSupplier.apply((int) (long) seed); NoiseFunction sampler = samplerSupplier.apply((int) (long) seed + salt);
sampler.setFrequency(frequency); sampler.setFrequency(frequency);
return sampler; return sampler;
} }

View File

@ -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.OpenSimplex2SSampler;
import com.dfsek.terra.api.math.noise.samplers.noise.simplex.OpenSimplex2Sampler; 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.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.ValueCubicSampler;
import com.dfsek.terra.api.math.noise.samplers.noise.value.ValueSampler; import com.dfsek.terra.api.math.noise.samplers.noise.value.ValueSampler;
import com.dfsek.terra.api.util.seeded.NoiseSeeded; 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.CellularNoiseTemplate;
import com.dfsek.terra.config.loaders.config.sampler.templates.noise.ConstantNoiseTemplate; 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.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.SimpleNoiseTemplate;
import com.dfsek.terra.config.loaders.config.sampler.templates.noise.fractal.BrownianMotionTemplate; import com.dfsek.terra.config.loaders.config.sampler.templates.noise.fractal.BrownianMotionTemplate;
import com.dfsek.terra.config.loaders.config.sampler.templates.noise.fractal.PingPongTemplate; import com.dfsek.terra.config.loaders.config.sampler.templates.noise.fractal.PingPongTemplate;
@ -44,6 +46,8 @@ public class NoiseRegistry extends OpenRegistry<Supplier<ObjectTemplate<NoiseSee
add("OPENSIMPLEX2", () -> new SimpleNoiseTemplate(OpenSimplex2Sampler::new)); add("OPENSIMPLEX2", () -> new SimpleNoiseTemplate(OpenSimplex2Sampler::new));
add("OPENSIMPLEX2S", () -> new SimpleNoiseTemplate(OpenSimplex2SSampler::new)); add("OPENSIMPLEX2S", () -> new SimpleNoiseTemplate(OpenSimplex2SSampler::new));
add("PERLIN", () -> new SimpleNoiseTemplate(PerlinSampler::new)); add("PERLIN", () -> new SimpleNoiseTemplate(PerlinSampler::new));
add("SIMPLEX", () -> new SimpleNoiseTemplate(SimplexSampler::new));
add("GABOR", GaborNoiseTemplate::new);
add("VALUE", () -> new SimpleNoiseTemplate(ValueSampler::new)); add("VALUE", () -> new SimpleNoiseTemplate(ValueSampler::new));