mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-12 03:24:14 +00:00
Merged noise refinement
- Existing noise corrections and fifteen new styles are integrated
This commit is contained in:
+1
-1
@@ -87,7 +87,7 @@ import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
private static final int SLAB_DEPTH = 128;
|
||||
private static final int SLAB_DEPTH = 512;
|
||||
private static final int SLAB_MIN_Y = 64;
|
||||
private static final int SLAB_PADDING = 14;
|
||||
private static final int SLAB_WIDTH = 16;
|
||||
|
||||
@@ -21,6 +21,7 @@ package art.arcane.iris.engine.object;
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.engine.data.cache.LazyBoundedCache;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.history.GenerationKernelRegistry;
|
||||
import art.arcane.iris.engine.object.annotations.Desc;
|
||||
import art.arcane.iris.engine.object.annotations.MaxNumber;
|
||||
import art.arcane.iris.engine.object.annotations.MinNumber;
|
||||
@@ -40,6 +41,7 @@ import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@@ -65,7 +67,7 @@ public class IrisGeneratorStyle {
|
||||
@Desc("Cell zooms")
|
||||
private double cellularZoom = 1;
|
||||
@MinNumber(0.00001)
|
||||
@Desc("The zoom of this style")
|
||||
@Desc("Feature size multiplier. Built-in coherent noise uses a 64-block base scale; 2 doubles feature size.")
|
||||
private double zoom = 1;
|
||||
@Desc("Instead of using the style property, use a custom expression to represent this style.")
|
||||
@RegistryListResource(IrisExpression.class)
|
||||
@@ -97,11 +99,15 @@ public class IrisGeneratorStyle {
|
||||
}
|
||||
|
||||
public CNG createNoCache(RNG rng, IrisData data) {
|
||||
return createNoCache(rng, data, false, 0, false, resolveEngine(data));
|
||||
return createNoCache(rng, data, 1, 0, false, resolveEngine(data));
|
||||
}
|
||||
|
||||
public CNG createForLayer(RNG rng, IrisData data, int octaveMultiplier) {
|
||||
return createNoCache(rng, data, Math.clamp(octaveMultiplier, 1, 16), 0, false, resolveEngine(data));
|
||||
}
|
||||
|
||||
public CNG createForPrebake(RNG rng, IrisData data, int fallbackCacheSize) {
|
||||
return createNoCache(rng, data, false, Math.max(0, fallbackCacheSize), true, resolveEngine(data));
|
||||
return createNoCache(rng, data, 1, Math.max(0, fallbackCacheSize), true, resolveEngine(data));
|
||||
}
|
||||
|
||||
|
||||
@@ -121,16 +127,17 @@ public class IrisGeneratorStyle {
|
||||
return hash();
|
||||
}
|
||||
|
||||
private String cachePrefix(RNG rng, int effectiveCacheSize, long engineStamp) {
|
||||
return "style-" + Integer.toUnsignedString(hash())
|
||||
private String cachePrefix(RNG rng, int effectiveCacheSize, long engineStamp, int octaveMultiplier) {
|
||||
return "noise-" + NoiseCacheIdentity.FINGERPRINT + "-style-" + Integer.toUnsignedString(hash())
|
||||
+ "-seed-" + Long.toUnsignedString(rng.getSeed())
|
||||
+ "-eng-" + Long.toUnsignedString(engineStamp)
|
||||
+ "-oct-" + octaveMultiplier
|
||||
+ "-sz-" + effectiveCacheSize
|
||||
+ "-src-";
|
||||
}
|
||||
|
||||
private String cacheKey(RNG rng, long sourceStamp, int effectiveCacheSize, long engineStamp) {
|
||||
return cachePrefix(rng, effectiveCacheSize, engineStamp) + Long.toUnsignedString(sourceStamp);
|
||||
private String cacheKey(RNG rng, long sourceStamp, int effectiveCacheSize, long engineStamp, int octaveMultiplier) {
|
||||
return cachePrefix(rng, effectiveCacheSize, engineStamp, octaveMultiplier) + Long.toUnsignedString(sourceStamp);
|
||||
}
|
||||
|
||||
private void clearStaleCacheEntries(IrisData data, String prefix, String key) {
|
||||
@@ -157,12 +164,19 @@ public class IrisGeneratorStyle {
|
||||
}
|
||||
}
|
||||
|
||||
public CNG createNoCache(RNG rng, IrisData data, boolean actuallyCached) {
|
||||
return createNoCache(rng, data, actuallyCached, 0, false, resolveEngine(data));
|
||||
}
|
||||
|
||||
private CNG createNoCache(RNG rng, IrisData data, boolean actuallyCached, int fallbackCacheSize,
|
||||
private CNG createNoCache(RNG rng, IrisData data, int octaveMultiplier, int fallbackCacheSize,
|
||||
boolean quietCacheLog, Engine engine) {
|
||||
requirePositiveFinite("zoom", zoom);
|
||||
requirePositiveFinite("exponent", exponent);
|
||||
if (!Double.isFinite(cellularFrequency) || cellularFrequency < 0D) {
|
||||
throw new IllegalArgumentException("cellularFrequency must be finite and nonnegative");
|
||||
}
|
||||
if (cellularFrequency > 0D) {
|
||||
requirePositiveFinite("cellularZoom", cellularZoom);
|
||||
}
|
||||
if (fracture != null) {
|
||||
requirePositiveFinite("fracture.multiplier", fracture.getMultiplier());
|
||||
}
|
||||
CNG cng = null;
|
||||
long sourceStamp = 0L;
|
||||
if (getExpression() != null) {
|
||||
@@ -182,10 +196,11 @@ public class IrisGeneratorStyle {
|
||||
cng = (style != null ? style : NoiseStyle.FLAT).create(rng).bake();
|
||||
}
|
||||
|
||||
cng = cng.scale(1D / zoom).pow(exponent).bake();
|
||||
cng = cng.oct(Math.clamp((long) cng.getOct() * octaveMultiplier, 1, 16))
|
||||
.pow(cng.getPower() * exponent);
|
||||
|
||||
if (fracture != null) {
|
||||
cng.fractureWith(fracture.createNoCache(rng.nextParallelRNG(2934), data, false,
|
||||
cng.fractureWith(fracture.createNoCache(rng.nextParallelRNG(2934), data, 1,
|
||||
fallbackCacheSize, quietCacheLog, engine), fracture.getMultiplier());
|
||||
}
|
||||
|
||||
@@ -193,11 +208,13 @@ public class IrisGeneratorStyle {
|
||||
cng = cng.cellularize(rng.nextParallelRNG(884466), cellularFrequency).scale(1D / cellularZoom).bake();
|
||||
}
|
||||
|
||||
cng.zoom(zoom);
|
||||
|
||||
int effectiveCacheSize = cacheSize > 0 ? cacheSize : Math.max(0, fallbackCacheSize);
|
||||
if (effectiveCacheSize > 0) {
|
||||
long engineStamp = engineStamp(engine);
|
||||
String key = cacheKey(rng, sourceStamp, effectiveCacheSize, engineStamp);
|
||||
clearStaleCacheEntries(data, cachePrefix(rng, effectiveCacheSize, engineStamp), key);
|
||||
String key = cacheKey(rng, sourceStamp, effectiveCacheSize, engineStamp, octaveMultiplier);
|
||||
clearStaleCacheEntries(data, cachePrefix(rng, effectiveCacheSize, engineStamp, octaveMultiplier), key);
|
||||
cng = cng.cached(effectiveCacheSize, key, data.getDataFolder(), quietCacheLog);
|
||||
}
|
||||
|
||||
@@ -215,7 +232,7 @@ public class IrisGeneratorStyle {
|
||||
public CNG create(RNG rng, IrisData data, Engine engine) {
|
||||
GeneratorCacheKey key = new GeneratorCacheKey(data, engine, rng.getSeed());
|
||||
return generatorCache.computeIfAbsent(key,
|
||||
ignored -> createNoCache(rng, data, true, 0, false, engine));
|
||||
ignored -> createNoCache(rng, data, 1, 0, false, engine));
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
@@ -244,6 +261,25 @@ public class IrisGeneratorStyle {
|
||||
return data == null ? null : data.getEngine();
|
||||
}
|
||||
|
||||
private static void requirePositiveFinite(String field, double value) {
|
||||
if (!Double.isFinite(value) || value <= 0D || !Double.isFinite(1D / value)) {
|
||||
throw new IllegalArgumentException(field + " must be finite and positive");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class NoiseCacheIdentity {
|
||||
private static final String FINGERPRINT = fingerprint();
|
||||
|
||||
private static String fingerprint() {
|
||||
GenerationKernelRegistry registry = GenerationKernelRegistry.standard();
|
||||
try {
|
||||
return registry.requireSupported(registry.current()).implementationFingerprint();
|
||||
} catch (IOException failure) {
|
||||
throw new IllegalStateException("Cannot identify the noise cache implementation", failure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class GeneratorCacheKey {
|
||||
private final IrisData data;
|
||||
private final Engine engine;
|
||||
|
||||
@@ -90,7 +90,8 @@ public class IrisNoiseGenerator {
|
||||
@Desc("The Noise Style")
|
||||
private IrisGeneratorStyle style = NoiseStyle.IRIS.style();
|
||||
@MinNumber(1)
|
||||
@Desc("Multiple octaves for multple generators of changing zooms added together")
|
||||
@MaxNumber(16)
|
||||
@Desc("Multiplies the style's octave count, capped at 16. Adds finer detail while preserving the base feature scale.")
|
||||
private int octaves = 1;
|
||||
@ArrayType(min = 1, type = IrisNoiseGenerator.class)
|
||||
@Desc("Apply a child noise generator to fracture the input coordinates of this generator")
|
||||
@@ -111,7 +112,7 @@ public class IrisNoiseGenerator {
|
||||
|
||||
GeneratorKey key = new GeneratorKey(data, engine, generatorSeed);
|
||||
CNG generator = generators.computeIfAbsent(key,
|
||||
ignored -> style.createNoCache(new RNG(generatorSeed), data).oct(octaves));
|
||||
ignored -> style.createForLayer(new RNG(generatorSeed), data, octaves));
|
||||
if (generator != null) {
|
||||
recentGenerator.set(new CachedGenerator(key, generator));
|
||||
}
|
||||
@@ -146,7 +147,7 @@ public class IrisNoiseGenerator {
|
||||
double n = cng.noiseFast2D(sampleX, sampleZ) * opacity;
|
||||
n = negative ? (-n + opacity) : n;
|
||||
n = (exponent != 1 ? n < 0 ? -Math.pow(-n, exponent) : Math.pow(n, exponent) : n) + offsetY;
|
||||
n = parametric ? IrisInterpolation.parametric(n, 1) : n;
|
||||
n = parametric ? IrisInterpolation.parametric(n, 2D) : n;
|
||||
n = bezier ? IrisInterpolation.bezier(n) : n;
|
||||
n = sinCentered ? IrisInterpolation.sinCenter(n) : n;
|
||||
|
||||
|
||||
@@ -27,523 +27,574 @@ import art.arcane.iris.util.project.stream.ProceduralStream;
|
||||
@Desc("Styles of noise")
|
||||
public enum NoiseStyle {
|
||||
@Desc("White Noise is like static. Useful for block scattering but not terrain.")
|
||||
STATIC(rng -> new CNG(rng, NoiseType.WHITE, 1D, 1)),
|
||||
STATIC(rng -> new CNG(rng, NoiseType.WHITE, 1D, 1), NoiseType.WHITE),
|
||||
|
||||
@Desc("White Noise is like static. Useful for block scattering but not terrain.")
|
||||
STATIC_BILINEAR(rng -> new CNG(rng, NoiseType.WHITE_BILINEAR, 1D, 1)),
|
||||
STATIC_BILINEAR(rng -> new CNG(rng, NoiseType.WHITE_BILINEAR, 1D, 1), NoiseType.WHITE_BILINEAR),
|
||||
|
||||
@Desc("White Noise is like static. Useful for block scattering but not terrain.")
|
||||
STATIC_BICUBIC(rng -> new CNG(rng, NoiseType.WHITE_BICUBIC, 1D, 1)),
|
||||
STATIC_BICUBIC(rng -> new CNG(rng, NoiseType.WHITE_BICUBIC, 1D, 1), NoiseType.WHITE_BICUBIC),
|
||||
|
||||
@Desc("White Noise is like static. Useful for block scattering but not terrain.")
|
||||
STATIC_HERMITE(rng -> new CNG(rng, NoiseType.WHITE_HERMITE, 1D, 1)),
|
||||
STATIC_HERMITE(rng -> new CNG(rng, NoiseType.WHITE_HERMITE, 1D, 1), NoiseType.WHITE_HERMITE),
|
||||
|
||||
@Desc("Wispy Perlin-looking simplex noise. The 'iris' style noise.")
|
||||
IRIS(rng -> CNG.signature(rng).scale(1)),
|
||||
IRIS(rng -> CNG.signature(rng)),
|
||||
|
||||
@Desc("Clover Noise")
|
||||
CLOVER(rng -> new CNG(rng, NoiseType.CLOVER, 1D, 1).scale(0.06).bake()),
|
||||
CLOVER(rng -> new CNG(rng, NoiseType.CLOVER, 1D, 1).bake(), NoiseType.CLOVER),
|
||||
|
||||
@Desc("CLOVER noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CLOVER_STARCAST_3(rng -> new CNG(rng, NoiseType.CLOVER_STARCAST_3, 1D, 1)),
|
||||
@Desc("Clover noise smoothed with the selected interpolation kernel.")
|
||||
CLOVER_STARCAST_3(rng -> new CNG(rng, NoiseType.CLOVER_STARCAST_3, 1D, 1), NoiseType.CLOVER_STARCAST_3),
|
||||
|
||||
@Desc("CLOVER noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CLOVER_STARCAST_6(rng -> new CNG(rng, NoiseType.CLOVER_STARCAST_6, 1D, 1)),
|
||||
@Desc("Clover noise smoothed with the selected interpolation kernel.")
|
||||
CLOVER_STARCAST_6(rng -> new CNG(rng, NoiseType.CLOVER_STARCAST_6, 1D, 1), NoiseType.CLOVER_STARCAST_6),
|
||||
|
||||
@Desc("CLOVER noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CLOVER_STARCAST_9(rng -> new CNG(rng, NoiseType.CLOVER_STARCAST_9, 1D, 1)),
|
||||
@Desc("Clover noise smoothed with the selected interpolation kernel.")
|
||||
CLOVER_STARCAST_9(rng -> new CNG(rng, NoiseType.CLOVER_STARCAST_9, 1D, 1), NoiseType.CLOVER_STARCAST_9),
|
||||
|
||||
@Desc("CLOVER noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CLOVER_STARCAST_12(rng -> new CNG(rng, NoiseType.CLOVER_STARCAST_12, 1D, 1)),
|
||||
@Desc("Clover noise smoothed with the selected interpolation kernel.")
|
||||
CLOVER_STARCAST_12(rng -> new CNG(rng, NoiseType.CLOVER_STARCAST_12, 1D, 1), NoiseType.CLOVER_STARCAST_12),
|
||||
|
||||
@Desc("CLOVER noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CLOVER_BILINEAR_STARCAST_3(rng -> new CNG(rng, NoiseType.CLOVER_BILINEAR_STARCAST_3, 1D, 1)),
|
||||
@Desc("Clover noise smoothed with the selected interpolation kernel.")
|
||||
CLOVER_BILINEAR_STARCAST_3(rng -> new CNG(rng, NoiseType.CLOVER_BILINEAR_STARCAST_3, 1D, 1), NoiseType.CLOVER_BILINEAR_STARCAST_3),
|
||||
|
||||
@Desc("CLOVER noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CLOVER_BILINEAR_STARCAST_6(rng -> new CNG(rng, NoiseType.CLOVER_BILINEAR_STARCAST_6, 1D, 1)),
|
||||
@Desc("Clover noise smoothed with the selected interpolation kernel.")
|
||||
CLOVER_BILINEAR_STARCAST_6(rng -> new CNG(rng, NoiseType.CLOVER_BILINEAR_STARCAST_6, 1D, 1), NoiseType.CLOVER_BILINEAR_STARCAST_6),
|
||||
|
||||
@Desc("CLOVER noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CLOVER_BILINEAR_STARCAST_9(rng -> new CNG(rng, NoiseType.CLOVER_BILINEAR_STARCAST_9, 1D, 1)),
|
||||
@Desc("Clover noise smoothed with the selected interpolation kernel.")
|
||||
CLOVER_BILINEAR_STARCAST_9(rng -> new CNG(rng, NoiseType.CLOVER_BILINEAR_STARCAST_9, 1D, 1), NoiseType.CLOVER_BILINEAR_STARCAST_9),
|
||||
|
||||
@Desc("CLOVER noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CLOVER_BILINEAR_STARCAST_12(rng -> new CNG(rng, NoiseType.CLOVER_BILINEAR_STARCAST_12, 1D, 1)),
|
||||
@Desc("Clover noise smoothed with the selected interpolation kernel.")
|
||||
CLOVER_BILINEAR_STARCAST_12(rng -> new CNG(rng, NoiseType.CLOVER_BILINEAR_STARCAST_12, 1D, 1), NoiseType.CLOVER_BILINEAR_STARCAST_12),
|
||||
|
||||
@Desc("CLOVER noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CLOVER_HERMITE_STARCAST_3(rng -> new CNG(rng, NoiseType.CLOVER_HERMITE_STARCAST_3, 1D, 1)),
|
||||
@Desc("Clover noise smoothed with the selected interpolation kernel.")
|
||||
CLOVER_HERMITE_STARCAST_3(rng -> new CNG(rng, NoiseType.CLOVER_HERMITE_STARCAST_3, 1D, 1), NoiseType.CLOVER_HERMITE_STARCAST_3),
|
||||
|
||||
@Desc("CLOVER noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CLOVER_HERMITE_STARCAST_6(rng -> new CNG(rng, NoiseType.CLOVER_HERMITE_STARCAST_6, 1D, 1)),
|
||||
@Desc("Clover noise smoothed with the selected interpolation kernel.")
|
||||
CLOVER_HERMITE_STARCAST_6(rng -> new CNG(rng, NoiseType.CLOVER_HERMITE_STARCAST_6, 1D, 1), NoiseType.CLOVER_HERMITE_STARCAST_6),
|
||||
|
||||
@Desc("CLOVER noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CLOVER_HERMITE_STARCAST_9(rng -> new CNG(rng, NoiseType.CLOVER_HERMITE_STARCAST_9, 1D, 1)),
|
||||
@Desc("Clover noise smoothed with the selected interpolation kernel.")
|
||||
CLOVER_HERMITE_STARCAST_9(rng -> new CNG(rng, NoiseType.CLOVER_HERMITE_STARCAST_9, 1D, 1), NoiseType.CLOVER_HERMITE_STARCAST_9),
|
||||
|
||||
@Desc("CLOVER noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CLOVER_HERMITE_STARCAST_12(rng -> new CNG(rng, NoiseType.CLOVER_HERMITE_STARCAST_12, 1D, 1)),
|
||||
@Desc("Clover noise smoothed with the selected interpolation kernel.")
|
||||
CLOVER_HERMITE_STARCAST_12(rng -> new CNG(rng, NoiseType.CLOVER_HERMITE_STARCAST_12, 1D, 1), NoiseType.CLOVER_HERMITE_STARCAST_12),
|
||||
|
||||
@Desc("CLOVER noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CLOVER_BILINEAR(rng -> new CNG(rng, NoiseType.CLOVER_BILINEAR, 1D, 1)),
|
||||
@Desc("Clover noise smoothed with the selected interpolation kernel.")
|
||||
CLOVER_BILINEAR(rng -> new CNG(rng, NoiseType.CLOVER_BILINEAR, 1D, 1), NoiseType.CLOVER_BILINEAR),
|
||||
|
||||
@Desc("CLOVER noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CLOVER_BICUBIC(rng -> new CNG(rng, NoiseType.CLOVER_BICUBIC, 1D, 1)),
|
||||
@Desc("Clover noise smoothed with the selected interpolation kernel.")
|
||||
CLOVER_BICUBIC(rng -> new CNG(rng, NoiseType.CLOVER_BICUBIC, 1D, 1), NoiseType.CLOVER_BICUBIC),
|
||||
|
||||
@Desc("CLOVER noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CLOVER_HERMITE(rng -> new CNG(rng, NoiseType.CLOVER_HERMITE, 1D, 1)),
|
||||
@Desc("Clover noise smoothed with the selected interpolation kernel.")
|
||||
CLOVER_HERMITE(rng -> new CNG(rng, NoiseType.CLOVER_HERMITE, 1D, 1), NoiseType.CLOVER_HERMITE),
|
||||
|
||||
@Desc("Vascular noise gets higher as the position nears a cell border.")
|
||||
VASCULAR(rng -> new CNG(rng, NoiseType.VASCULAR, 1D, 1)),
|
||||
VASCULAR(rng -> new CNG(rng, NoiseType.VASCULAR, 1D, 1), NoiseType.VASCULAR),
|
||||
|
||||
@Desc("It always returns 0.5")
|
||||
FLAT(rng -> new CNG(rng, NoiseType.FLAT, 1D, 1)),
|
||||
@Desc("It always returns 1.0")
|
||||
FLAT(rng -> new CNG(rng, NoiseType.FLAT, 1D, 1), NoiseType.FLAT),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CELLULAR(rng -> new CNG(rng, NoiseType.CELLULAR, 1D, 1)),
|
||||
CELLULAR(rng -> new CNG(rng, NoiseType.CELLULAR, 1D, 1), NoiseType.CELLULAR),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CELLULAR_STARCAST_3(rng -> new CNG(rng, NoiseType.CELLULAR_STARCAST_3, 1D, 1)),
|
||||
CELLULAR_STARCAST_3(rng -> new CNG(rng, NoiseType.CELLULAR_STARCAST_3, 1D, 1), NoiseType.CELLULAR_STARCAST_3),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CELLULAR_STARCAST_6(rng -> new CNG(rng, NoiseType.CELLULAR_STARCAST_6, 1D, 1)),
|
||||
CELLULAR_STARCAST_6(rng -> new CNG(rng, NoiseType.CELLULAR_STARCAST_6, 1D, 1), NoiseType.CELLULAR_STARCAST_6),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CELLULAR_STARCAST_9(rng -> new CNG(rng, NoiseType.CELLULAR_STARCAST_9, 1D, 1)),
|
||||
CELLULAR_STARCAST_9(rng -> new CNG(rng, NoiseType.CELLULAR_STARCAST_9, 1D, 1), NoiseType.CELLULAR_STARCAST_9),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CELLULAR_STARCAST_12(rng -> new CNG(rng, NoiseType.CELLULAR_STARCAST_12, 1D, 1)),
|
||||
CELLULAR_STARCAST_12(rng -> new CNG(rng, NoiseType.CELLULAR_STARCAST_12, 1D, 1), NoiseType.CELLULAR_STARCAST_12),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CELLULAR_BILINEAR_STARCAST_3(rng -> new CNG(rng, NoiseType.CELLULAR_BILINEAR_STARCAST_3, 1D, 1)),
|
||||
CELLULAR_BILINEAR_STARCAST_3(rng -> new CNG(rng, NoiseType.CELLULAR_BILINEAR_STARCAST_3, 1D, 1), NoiseType.CELLULAR_BILINEAR_STARCAST_3),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CELLULAR_BILINEAR_STARCAST_6(rng -> new CNG(rng, NoiseType.CELLULAR_BILINEAR_STARCAST_6, 1D, 1)),
|
||||
CELLULAR_BILINEAR_STARCAST_6(rng -> new CNG(rng, NoiseType.CELLULAR_BILINEAR_STARCAST_6, 1D, 1), NoiseType.CELLULAR_BILINEAR_STARCAST_6),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CELLULAR_BILINEAR_STARCAST_9(rng -> new CNG(rng, NoiseType.CELLULAR_BILINEAR_STARCAST_9, 1D, 1)),
|
||||
CELLULAR_BILINEAR_STARCAST_9(rng -> new CNG(rng, NoiseType.CELLULAR_BILINEAR_STARCAST_9, 1D, 1), NoiseType.CELLULAR_BILINEAR_STARCAST_9),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CELLULAR_BILINEAR_STARCAST_12(rng -> new CNG(rng, NoiseType.CELLULAR_BILINEAR_STARCAST_12, 1D, 1)),
|
||||
CELLULAR_BILINEAR_STARCAST_12(rng -> new CNG(rng, NoiseType.CELLULAR_BILINEAR_STARCAST_12, 1D, 1), NoiseType.CELLULAR_BILINEAR_STARCAST_12),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CELLULAR_HERMITE_STARCAST_3(rng -> new CNG(rng, NoiseType.CELLULAR_HERMITE_STARCAST_3, 1D, 1)),
|
||||
CELLULAR_HERMITE_STARCAST_3(rng -> new CNG(rng, NoiseType.CELLULAR_HERMITE_STARCAST_3, 1D, 1), NoiseType.CELLULAR_HERMITE_STARCAST_3),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CELLULAR_HERMITE_STARCAST_6(rng -> new CNG(rng, NoiseType.CELLULAR_HERMITE_STARCAST_6, 1D, 1)),
|
||||
CELLULAR_HERMITE_STARCAST_6(rng -> new CNG(rng, NoiseType.CELLULAR_HERMITE_STARCAST_6, 1D, 1), NoiseType.CELLULAR_HERMITE_STARCAST_6),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CELLULAR_HERMITE_STARCAST_9(rng -> new CNG(rng, NoiseType.CELLULAR_HERMITE_STARCAST_9, 1D, 1)),
|
||||
CELLULAR_HERMITE_STARCAST_9(rng -> new CNG(rng, NoiseType.CELLULAR_HERMITE_STARCAST_9, 1D, 1), NoiseType.CELLULAR_HERMITE_STARCAST_9),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CELLULAR_HERMITE_STARCAST_12(rng -> new CNG(rng, NoiseType.CELLULAR_HERMITE_STARCAST_12, 1D, 1)),
|
||||
CELLULAR_HERMITE_STARCAST_12(rng -> new CNG(rng, NoiseType.CELLULAR_HERMITE_STARCAST_12, 1D, 1), NoiseType.CELLULAR_HERMITE_STARCAST_12),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CELLULAR_BILINEAR(rng -> new CNG(rng, NoiseType.CELLULAR_BILINEAR, 1D, 1)),
|
||||
CELLULAR_BILINEAR(rng -> new CNG(rng, NoiseType.CELLULAR_BILINEAR, 1D, 1), NoiseType.CELLULAR_BILINEAR),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CELLULAR_BICUBIC(rng -> new CNG(rng, NoiseType.CELLULAR_BICUBIC, 1D, 1)),
|
||||
CELLULAR_BICUBIC(rng -> new CNG(rng, NoiseType.CELLULAR_BICUBIC, 1D, 1), NoiseType.CELLULAR_BICUBIC),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders.")
|
||||
CELLULAR_HERMITE(rng -> new CNG(rng, NoiseType.CELLULAR_HERMITE, 1D, 1)),
|
||||
CELLULAR_HERMITE(rng -> new CNG(rng, NoiseType.CELLULAR_HERMITE, 1D, 1), NoiseType.CELLULAR_HERMITE),
|
||||
|
||||
@Desc("Hexagonal cells with stable per-cell values.")
|
||||
HEXAGON(rng -> new CNG(rng, NoiseType.HEXAGON, 1D, 1).scale(1)),
|
||||
@Desc("Solid regular hexagons colored by a coherent simplex field.")
|
||||
HEXAGON(rng -> new CNG(rng, NoiseType.HEXAGON, 1D, 1), NoiseType.HEXAGON),
|
||||
|
||||
@Desc("Hex James substitution pattern with 3 large and 3 small recursive hex children plus gaps.")
|
||||
HEX_JAMES(rng -> new CNG(rng, NoiseType.HEX_JAMES, 1D, 1).scale(1)),
|
||||
@Desc("Recursive contained hexagons with alternating subdivision probabilities and simplex colors.")
|
||||
HEX_JAMES(rng -> new CNG(rng, NoiseType.HEX_JAMES, 1D, 1), NoiseType.HEX_JAMES),
|
||||
|
||||
@Desc("Interlocked solid hex cells with per-cell values from a smooth simplex heatmap.")
|
||||
HEX_SIMPLEX(rng -> new CNG(rng, NoiseType.HEX_SIMPLEX, 1D, 1).scale(1)),
|
||||
HEX_SIMPLEX(rng -> new CNG(rng, NoiseType.HEX_SIMPLEX, 1D, 1), NoiseType.HEX_SIMPLEX),
|
||||
|
||||
@Desc("Randomized finite-depth Sierpinski-style hex recursion with per-level random 3-large/3-small James substitution.")
|
||||
HEX_RANDOM_SIZE(rng -> new CNG(rng, NoiseType.HEX_RANDOM_SIZE, 1D, 1).scale(1)),
|
||||
@Desc("Finite-depth hexagonal subdivision selected by a seeded field, with coherent simplex colors.")
|
||||
HEX_RANDOM_SIZE(rng -> new CNG(rng, NoiseType.HEX_RANDOM_SIZE, 1D, 1), NoiseType.HEX_RANDOM_SIZE),
|
||||
|
||||
@Desc("Sierpinski triangle mask blended with simplex heat.")
|
||||
SIERPINSKI_TRIANGLE(rng -> new CNG(rng, NoiseType.SIERPINSKI_TRIANGLE, 1D, 1).scale(1)),
|
||||
@Desc("Finite-depth equilateral Sierpinski triangles colored by simplex heat.")
|
||||
SIERPINSKI_TRIANGLE(rng -> new CNG(rng, NoiseType.SIERPINSKI_TRIANGLE, 1D, 1), NoiseType.SIERPINSKI_TRIANGLE),
|
||||
|
||||
@Desc("Classic German Engineering")
|
||||
NOWHERE(rng -> CNG.signaturePerlin(rng).scale(0.776).bake()),
|
||||
@Desc("Perlin-warped noise with broad, flowing distortion.")
|
||||
NOWHERE(rng -> CNG.signaturePerlin(rng).bake()),
|
||||
|
||||
@Desc("Classic German Engineering")
|
||||
NOWHERE_CELLULAR(rng -> CNG.signaturePerlin(rng, NoiseType.CELLULAR).scale(1).bake()),
|
||||
@Desc("Perlin-warped noise with broad, flowing distortion.")
|
||||
NOWHERE_CELLULAR(rng -> CNG.signaturePerlin(rng, NoiseType.CELLULAR).bake(), NoiseType.CELLULAR),
|
||||
|
||||
@Desc("Classic German Engineering")
|
||||
NOWHERE_CLOVER(rng -> CNG.signaturePerlin(rng, NoiseType.CLOVER).scale(1).bake()),
|
||||
@Desc("Perlin-warped noise with broad, flowing distortion.")
|
||||
NOWHERE_CLOVER(rng -> CNG.signaturePerlin(rng, NoiseType.CLOVER).bake(), NoiseType.CLOVER),
|
||||
|
||||
@Desc("Classic German Engineering")
|
||||
NOWHERE_HEXAGON(rng -> CNG.signaturePerlin(rng, NoiseType.HEXAGON).scale(1).bake()),
|
||||
@Desc("Perlin-warped noise with broad, flowing distortion.")
|
||||
NOWHERE_HEXAGON(rng -> CNG.signaturePerlin(rng, NoiseType.HEXAGON).bake(), NoiseType.HEXAGON),
|
||||
|
||||
@Desc("Classic German Engineering")
|
||||
NOWHERE_HEX_JAMES(rng -> CNG.signaturePerlin(rng, NoiseType.HEX_JAMES).scale(1).bake()),
|
||||
@Desc("Perlin-warped noise with broad, flowing distortion.")
|
||||
NOWHERE_HEX_JAMES(rng -> CNG.signaturePerlin(rng, NoiseType.HEX_JAMES).bake(), NoiseType.HEX_JAMES),
|
||||
|
||||
@Desc("Classic German Engineering")
|
||||
NOWHERE_HEX_SIMPLEX(rng -> CNG.signaturePerlin(rng, NoiseType.HEX_SIMPLEX).scale(1).bake()),
|
||||
@Desc("Perlin-warped noise with broad, flowing distortion.")
|
||||
NOWHERE_HEX_SIMPLEX(rng -> CNG.signaturePerlin(rng, NoiseType.HEX_SIMPLEX).bake(), NoiseType.HEX_SIMPLEX),
|
||||
|
||||
@Desc("Classic German Engineering")
|
||||
NOWHERE_HEX_RANDOM_SIZE(rng -> CNG.signaturePerlin(rng, NoiseType.HEX_RANDOM_SIZE).scale(1).bake()),
|
||||
@Desc("Perlin-warped noise with broad, flowing distortion.")
|
||||
NOWHERE_HEX_RANDOM_SIZE(rng -> CNG.signaturePerlin(rng, NoiseType.HEX_RANDOM_SIZE).bake(), NoiseType.HEX_RANDOM_SIZE),
|
||||
|
||||
@Desc("Classic German Engineering")
|
||||
NOWHERE_SIERPINSKI_TRIANGLE(rng -> CNG.signaturePerlin(rng, NoiseType.SIERPINSKI_TRIANGLE).scale(1).bake()),
|
||||
@Desc("Perlin-warped noise with broad, flowing distortion.")
|
||||
NOWHERE_SIERPINSKI_TRIANGLE(rng -> CNG.signaturePerlin(rng, NoiseType.SIERPINSKI_TRIANGLE).bake(), NoiseType.SIERPINSKI_TRIANGLE),
|
||||
|
||||
@Desc("Classic German Engineering")
|
||||
NOWHERE_SIMPLEX(rng -> CNG.signaturePerlin(rng, NoiseType.SIMPLEX).scale(1).bake()),
|
||||
@Desc("Perlin-warped noise with broad, flowing distortion.")
|
||||
NOWHERE_SIMPLEX(rng -> CNG.signaturePerlin(rng, NoiseType.SIMPLEX).bake(), NoiseType.SIMPLEX),
|
||||
|
||||
@Desc("Classic German Engineering")
|
||||
NOWHERE_GLOB(rng -> CNG.signaturePerlin(rng, NoiseType.GLOB).scale(1).bake()),
|
||||
@Desc("Perlin-warped noise with broad, flowing distortion.")
|
||||
NOWHERE_GLOB(rng -> CNG.signaturePerlin(rng, NoiseType.GLOB).bake(), NoiseType.GLOB),
|
||||
|
||||
@Desc("Classic German Engineering")
|
||||
NOWHERE_VASCULAR(rng -> CNG.signaturePerlin(rng, NoiseType.VASCULAR).scale(1).bake()),
|
||||
@Desc("Perlin-warped noise with broad, flowing distortion.")
|
||||
NOWHERE_VASCULAR(rng -> CNG.signaturePerlin(rng, NoiseType.VASCULAR).bake(), NoiseType.VASCULAR),
|
||||
|
||||
@Desc("Classic German Engineering")
|
||||
NOWHERE_CUBIC(rng -> CNG.signaturePerlin(rng, NoiseType.CUBIC).scale(1).bake()),
|
||||
@Desc("Perlin-warped noise with broad, flowing distortion.")
|
||||
NOWHERE_CUBIC(rng -> CNG.signaturePerlin(rng, NoiseType.CUBIC).bake(), NoiseType.CUBIC),
|
||||
|
||||
@Desc("Classic German Engineering")
|
||||
NOWHERE_SUPERFRACTAL(rng -> CNG.signaturePerlin(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX).scale(1).bake()),
|
||||
@Desc("Perlin-warped noise with broad, flowing distortion.")
|
||||
NOWHERE_SUPERFRACTAL(rng -> CNG.signaturePerlin(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX).bake(), NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX),
|
||||
|
||||
@Desc("Classic German Engineering")
|
||||
NOWHERE_FRACTAL(rng -> CNG.signaturePerlin(rng, NoiseType.FRACTAL_BILLOW_PERLIN).scale(1).bake()),
|
||||
@Desc("Perlin-warped noise with broad, flowing distortion.")
|
||||
NOWHERE_FRACTAL(rng -> CNG.signaturePerlin(rng, NoiseType.FRACTAL_BILLOW_PERLIN).bake(), NoiseType.FRACTAL_BILLOW_PERLIN),
|
||||
|
||||
@Desc("Wispy Perlin-looking simplex noise. The 'iris' style noise.")
|
||||
IRIS_DOUBLE(rng -> CNG.signatureDouble(rng).scale(1)),
|
||||
IRIS_DOUBLE(rng -> CNG.signatureDouble(rng)),
|
||||
|
||||
@Desc("Wispy Perlin-looking simplex noise. The 'iris' style noise.")
|
||||
IRIS_THICK(rng -> CNG.signatureThick(rng).scale(1)),
|
||||
IRIS_THICK(rng -> CNG.signatureThick(rng)),
|
||||
|
||||
@Desc("Wispy Perlin-looking simplex noise. The 'iris' style noise.")
|
||||
IRIS_HALF(rng -> CNG.signatureHalf(rng).scale(1)),
|
||||
IRIS_HALF(rng -> CNG.signatureHalf(rng)),
|
||||
|
||||
@Desc("Basic, Smooth & Fast Simplex noise.")
|
||||
SIMPLEX(rng -> new CNG(rng, 1D, 1).scale(1)),
|
||||
SIMPLEX(rng -> new CNG(rng, 1D, 1)),
|
||||
|
||||
@Desc("Very Detailed smoke using simplex fractured with fractal billow simplex at high octaves.")
|
||||
FRACTAL_SMOKE(rng -> new CNG(rng, 1D, 1).fractureWith(new CNG(rng.nextParallelRNG(1), NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 8).scale(0.2), 1000).scale(0.34)),
|
||||
FRACTAL_SMOKE(rng -> new CNG(rng, 1D, 1).fractureWith(new CNG(rng.nextParallelRNG(1), NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 8).scale(0.2), 1000), NoiseType.FRACTAL_BILLOW_SIMPLEX),
|
||||
|
||||
@Desc("Thinner Veins.")
|
||||
VASCULAR_THIN(rng -> new CNG(rng.nextParallelRNG(1), NoiseType.VASCULAR, 1D, 1).scale(1).pow(0.65)),
|
||||
VASCULAR_THIN(rng -> new CNG(rng.nextParallelRNG(1), NoiseType.VASCULAR, 1D, 1).scale(1).pow(1D / 0.65D), NoiseType.VASCULAR),
|
||||
|
||||
@Desc("Cells of simplex noise")
|
||||
SIMPLEX_CELLS(rng -> new CNG(rng.nextParallelRNG(1), NoiseType.SIMPLEX, 1D, 1).scale(1).fractureWith(new CNG(rng.nextParallelRNG(8), NoiseType.CELLULAR, 1D, 1).scale(1), 200)),
|
||||
SIMPLEX_CELLS(rng -> new CNG(rng.nextParallelRNG(1), NoiseType.SIMPLEX, 1D, 1).scale(1).fractureWith(new CNG(rng.nextParallelRNG(8), NoiseType.CELLULAR, 1D, 1).scale(1), 200), NoiseType.SIMPLEX),
|
||||
|
||||
@Desc("Veins of simplex noise")
|
||||
SIMPLEX_VASCULAR(rng -> new CNG(rng.nextParallelRNG(1), NoiseType.SIMPLEX, 1D, 1).scale(1).fractureWith(new CNG(rng.nextParallelRNG(8), NoiseType.VASCULAR, 1D, 1).scale(1), 200)),
|
||||
SIMPLEX_VASCULAR(rng -> new CNG(rng.nextParallelRNG(1), NoiseType.SIMPLEX, 1D, 1).scale(1).fractureWith(new CNG(rng.nextParallelRNG(8), NoiseType.VASCULAR, 1D, 1).scale(1), 200), NoiseType.SIMPLEX),
|
||||
|
||||
@Desc("Very Detailed fluid using simplex fractured with fractal billow simplex at high octaves.")
|
||||
FRACTAL_WATER(rng -> new CNG(rng, 1D, 1).fractureWith(new CNG(rng.nextParallelRNG(1), NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 9).scale(0.03), 9900).scale(1.14)),
|
||||
FRACTAL_WATER(rng -> new CNG(rng, 1D, 1).fractureWith(new CNG(rng.nextParallelRNG(1), NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 9).scale(0.03), 9900), NoiseType.FRACTAL_FBM_SIMPLEX),
|
||||
|
||||
@Desc("Perlin. Like simplex but more natural")
|
||||
PERLIN(rng -> new CNG(rng, NoiseType.PERLIN, 1D, 1).scale(1.15)),
|
||||
PERLIN(rng -> new CNG(rng, NoiseType.PERLIN, 1D, 1), NoiseType.PERLIN),
|
||||
|
||||
@Desc("Perlin. Like simplex but more natural")
|
||||
PERLIN_IRIS(rng -> CNG.signature(rng, NoiseType.PERLIN).scale(1.47)),
|
||||
PERLIN_IRIS(rng -> CNG.signature(rng, NoiseType.PERLIN), NoiseType.PERLIN),
|
||||
|
||||
@Desc("Perlin. Like simplex but more natural")
|
||||
PERLIN_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.PERLIN).scale(1.47)),
|
||||
PERLIN_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.PERLIN), NoiseType.PERLIN),
|
||||
|
||||
@Desc("Perlin. Like simplex but more natural")
|
||||
PERLIN_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.PERLIN).scale(1.47)),
|
||||
PERLIN_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.PERLIN), NoiseType.PERLIN),
|
||||
|
||||
@Desc("Perlin. Like simplex but more natural")
|
||||
PERLIN_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.PERLIN).scale(1.47)),
|
||||
PERLIN_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.PERLIN), NoiseType.PERLIN),
|
||||
|
||||
@Desc("Billow Fractal Perlin Noise.")
|
||||
FRACTAL_BILLOW_PERLIN(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_PERLIN, 1D, 1).scale(1.47)),
|
||||
FRACTAL_BILLOW_PERLIN(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_PERLIN, 1D, 1), NoiseType.FRACTAL_BILLOW_PERLIN),
|
||||
|
||||
@Desc("Billow Fractal Perlin Noise. 2 Octaves")
|
||||
BIOCTAVE_FRACTAL_BILLOW_PERLIN(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_PERLIN, 1D, 2).scale(1.17)),
|
||||
BIOCTAVE_FRACTAL_BILLOW_PERLIN(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_PERLIN, 1D, 2), NoiseType.FRACTAL_BILLOW_PERLIN),
|
||||
|
||||
@Desc("Billow Fractal Simplex Noise. Single octave.")
|
||||
FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 1)),
|
||||
FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 1), NoiseType.FRACTAL_BILLOW_SIMPLEX),
|
||||
|
||||
@Desc("FBM Fractal Simplex Noise. Single octave.")
|
||||
FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 1)),
|
||||
FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 1), NoiseType.FRACTAL_FBM_SIMPLEX),
|
||||
|
||||
@Desc("Billow Fractal Iris Noise. Single octave.")
|
||||
FRACTAL_BILLOW_IRIS(rng -> CNG.signature(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX)),
|
||||
FRACTAL_BILLOW_IRIS(rng -> CNG.signature(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX), NoiseType.FRACTAL_BILLOW_SIMPLEX),
|
||||
|
||||
@Desc("FBM Fractal Iris Noise. Single octave.")
|
||||
FRACTAL_FBM_IRIS(rng -> CNG.signature(rng, NoiseType.FRACTAL_FBM_SIMPLEX)),
|
||||
FRACTAL_FBM_IRIS(rng -> CNG.signature(rng, NoiseType.FRACTAL_FBM_SIMPLEX), NoiseType.FRACTAL_FBM_SIMPLEX),
|
||||
|
||||
@Desc("Billow Fractal Iris Noise. Single octave.")
|
||||
FRACTAL_BILLOW_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX)),
|
||||
FRACTAL_BILLOW_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX), NoiseType.FRACTAL_BILLOW_SIMPLEX),
|
||||
|
||||
@Desc("FBM Fractal Iris Noise. Single octave.")
|
||||
FRACTAL_FBM_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.FRACTAL_FBM_SIMPLEX)),
|
||||
FRACTAL_FBM_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.FRACTAL_FBM_SIMPLEX), NoiseType.FRACTAL_FBM_SIMPLEX),
|
||||
|
||||
@Desc("Billow Fractal Iris Noise. Single octave.")
|
||||
FRACTAL_BILLOW_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX)),
|
||||
FRACTAL_BILLOW_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX), NoiseType.FRACTAL_BILLOW_SIMPLEX),
|
||||
|
||||
@Desc("FBM Fractal Iris Noise. Single octave.")
|
||||
FRACTAL_FBM_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.FRACTAL_FBM_SIMPLEX)),
|
||||
FRACTAL_FBM_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.FRACTAL_FBM_SIMPLEX), NoiseType.FRACTAL_FBM_SIMPLEX),
|
||||
|
||||
@Desc("Fractal hexagonal cell noise.")
|
||||
FRACTAL_HEXAGON(rng -> new CNG(rng, NoiseType.HEXAGON, 1D, 4).scale(1)),
|
||||
FRACTAL_HEXAGON(rng -> new CNG(rng, NoiseType.HEXAGON, 1D, 4), NoiseType.HEXAGON),
|
||||
|
||||
@Desc("Fractal Hex James substitution pattern with recursive 3-large/3-small child hexes and gaps.")
|
||||
FRACTAL_HEX_JAMES(rng -> new CNG(rng, NoiseType.HEX_JAMES, 1D, 4).scale(1)),
|
||||
@Desc("Recursive contained hexagons colored by fractal simplex noise.")
|
||||
FRACTAL_HEX_JAMES(rng -> new CNG(rng, NoiseType.HEX_JAMES, 1D, 4), NoiseType.HEX_JAMES),
|
||||
|
||||
@Desc("Interlocked solid hex cells with per-cell fractal simplex heatmap values.")
|
||||
FRACTAL_HEX_SIMPLEX(rng -> new CNG(rng, NoiseType.HEX_SIMPLEX, 1D, 4).scale(1)),
|
||||
FRACTAL_HEX_SIMPLEX(rng -> new CNG(rng, NoiseType.HEX_SIMPLEX, 1D, 4), NoiseType.HEX_SIMPLEX),
|
||||
|
||||
@Desc("Fractal hexagonal gradient noise with randomized local hex sizes and blended random/simplex heatmaps.")
|
||||
FRACTAL_HEX_RANDOM_SIZE(rng -> new CNG(rng, NoiseType.HEX_RANDOM_SIZE, 1D, 4).scale(1)),
|
||||
@Desc("Recursive hexagonal subdivision colored by fractal simplex noise.")
|
||||
FRACTAL_HEX_RANDOM_SIZE(rng -> new CNG(rng, NoiseType.HEX_RANDOM_SIZE, 1D, 4), NoiseType.HEX_RANDOM_SIZE),
|
||||
|
||||
@Desc("Fractal Sierpinski triangle mask blended with simplex heat.")
|
||||
FRACTAL_SIERPINSKI_TRIANGLE(rng -> new CNG(rng, NoiseType.SIERPINSKI_TRIANGLE, 1D, 4).scale(1)),
|
||||
@Desc("Finite-depth equilateral Sierpinski triangles colored by fractal simplex heat.")
|
||||
FRACTAL_SIERPINSKI_TRIANGLE(rng -> new CNG(rng, NoiseType.SIERPINSKI_TRIANGLE, 1D, 4), NoiseType.SIERPINSKI_TRIANGLE),
|
||||
|
||||
@Desc("Rigid Multi Fractal Simplex Noise. Single octave.")
|
||||
FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 1)),
|
||||
FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 1), NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX),
|
||||
|
||||
@Desc("Billow Fractal Simplex Noise. 2 octaves.")
|
||||
BIOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 2)),
|
||||
BIOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 2), NoiseType.FRACTAL_BILLOW_SIMPLEX),
|
||||
|
||||
@Desc("FBM Fractal Simplex Noise. 2 octaves.")
|
||||
BIOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 2)),
|
||||
BIOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 2), NoiseType.FRACTAL_FBM_SIMPLEX),
|
||||
|
||||
@Desc("Rigid Multi Fractal Simplex Noise. 2 octaves.")
|
||||
BIOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 2)),
|
||||
BIOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 2), NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX),
|
||||
|
||||
@Desc("Rigid Multi Fractal Simplex Noise. 3 octaves.")
|
||||
TRIOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 3)),
|
||||
TRIOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 3), NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX),
|
||||
|
||||
@Desc("Billow Fractal Simplex Noise. 3 octaves.")
|
||||
TRIOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 3)),
|
||||
TRIOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 3), NoiseType.FRACTAL_BILLOW_SIMPLEX),
|
||||
|
||||
@Desc("FBM Fractal Simplex Noise. 3 octaves.")
|
||||
TRIOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 3)),
|
||||
TRIOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 3), NoiseType.FRACTAL_FBM_SIMPLEX),
|
||||
|
||||
@Desc("Rigid Multi Fractal Simplex Noise. 4 octaves.")
|
||||
QUADOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 4)),
|
||||
QUADOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 4), NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX),
|
||||
|
||||
@Desc("Billow Fractal Simplex Noise. 4 octaves.")
|
||||
QUADOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 4)),
|
||||
QUADOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 4), NoiseType.FRACTAL_BILLOW_SIMPLEX),
|
||||
|
||||
@Desc("FBM Fractal Simplex Noise. 4 octaves.")
|
||||
QUADOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 4)),
|
||||
QUADOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 4), NoiseType.FRACTAL_FBM_SIMPLEX),
|
||||
|
||||
@Desc("Rigid Multi Fractal Simplex Noise. 5 octaves.")
|
||||
QUINTOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 5)),
|
||||
QUINTOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 5), NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX),
|
||||
|
||||
@Desc("Billow Fractal Simplex Noise. 5 octaves.")
|
||||
QUINTOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 5)),
|
||||
QUINTOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 5), NoiseType.FRACTAL_BILLOW_SIMPLEX),
|
||||
|
||||
@Desc("FBM Fractal Simplex Noise. 5 octaves.")
|
||||
QUINTOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 5)),
|
||||
QUINTOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 5), NoiseType.FRACTAL_FBM_SIMPLEX),
|
||||
|
||||
@Desc("Rigid Multi Fractal Simplex Noise. 6 octaves.")
|
||||
SEXOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 6)),
|
||||
SEXOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 6), NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX),
|
||||
|
||||
@Desc("Billow Fractal Simplex Noise. 6 octaves.")
|
||||
SEXOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 6)),
|
||||
SEXOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 6), NoiseType.FRACTAL_BILLOW_SIMPLEX),
|
||||
|
||||
@Desc("FBM Fractal Simplex Noise. 6 octaves.")
|
||||
SEXOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 6)),
|
||||
SEXOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 6), NoiseType.FRACTAL_FBM_SIMPLEX),
|
||||
|
||||
@Desc("Rigid Multi Fractal Simplex Noise. 7 octaves.")
|
||||
SEPTOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 7)),
|
||||
SEPTOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 7), NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX),
|
||||
|
||||
@Desc("Billow Fractal Simplex Noise. 7 octaves.")
|
||||
SEPTOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 7)),
|
||||
SEPTOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 7), NoiseType.FRACTAL_BILLOW_SIMPLEX),
|
||||
|
||||
@Desc("FBM Fractal Simplex Noise. 7 octaves.")
|
||||
SEPTOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 7)),
|
||||
SEPTOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 7), NoiseType.FRACTAL_FBM_SIMPLEX),
|
||||
|
||||
@Desc("Rigid Multi Fractal Simplex Noise. 8 octaves.")
|
||||
OCTOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 8)),
|
||||
OCTOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 8), NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX),
|
||||
|
||||
@Desc("Billow Fractal Simplex Noise. 8 octaves.")
|
||||
OCTOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 8)),
|
||||
OCTOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 8), NoiseType.FRACTAL_BILLOW_SIMPLEX),
|
||||
|
||||
@Desc("FBM Fractal Simplex Noise. 8 octaves.")
|
||||
OCTOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 8)),
|
||||
OCTOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 8), NoiseType.FRACTAL_FBM_SIMPLEX),
|
||||
|
||||
@Desc("Rigid Multi Fractal Simplex Noise. 9 octaves.")
|
||||
NONOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 9)),
|
||||
NONOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 9), NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX),
|
||||
|
||||
@Desc("Billow Fractal Simplex Noise. 9 octaves.")
|
||||
NONOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 9)),
|
||||
NONOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 9), NoiseType.FRACTAL_BILLOW_SIMPLEX),
|
||||
|
||||
@Desc("FBM Fractal Simplex Noise. 9 octaves.")
|
||||
NONOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 9)),
|
||||
NONOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 9), NoiseType.FRACTAL_FBM_SIMPLEX),
|
||||
|
||||
@Desc("Rigid Multi Fractal Simplex Noise. 10 octaves.")
|
||||
VIGOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 10)),
|
||||
VIGOCTAVE_FRACTAL_RM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX, 1D, 10), NoiseType.FRACTAL_RIGID_MULTI_SIMPLEX),
|
||||
|
||||
@Desc("Billow Fractal Simplex Noise. 10 octaves.")
|
||||
VIGOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 10)),
|
||||
VIGOCTAVE_FRACTAL_BILLOW_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_BILLOW_SIMPLEX, 1D, 10), NoiseType.FRACTAL_BILLOW_SIMPLEX),
|
||||
|
||||
@Desc("FBM Fractal Simplex Noise. 10 octaves.")
|
||||
VIGOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 10)),
|
||||
VIGOCTAVE_FRACTAL_FBM_SIMPLEX(rng -> new CNG(rng, NoiseType.FRACTAL_FBM_SIMPLEX, 1D, 10), NoiseType.FRACTAL_FBM_SIMPLEX),
|
||||
|
||||
@Desc("Basic, Smooth & Fast Simplex noise. Uses 2 octaves")
|
||||
BIOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 2).scale(1D / 2D)),
|
||||
BIOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 2)),
|
||||
|
||||
@Desc("Basic, Smooth & Fast Simplex noise. Uses 3 octaves")
|
||||
TRIOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 3).scale(1D / 3D)),
|
||||
TRIOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 3)),
|
||||
|
||||
@Desc("Basic, Smooth & Fast Simplex noise. Uses 4 octaves")
|
||||
QUADOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 4).scale(1D / 4D)),
|
||||
QUADOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 4)),
|
||||
|
||||
@Desc("Basic, Smooth & Fast Simplex noise. Uses 5 octaves")
|
||||
QUINTOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 5).scale(1D / 5D)),
|
||||
QUINTOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 5)),
|
||||
|
||||
@Desc("Basic, Smooth & Fast Simplex noise. Uses 6 octaves")
|
||||
SEXOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 6).scale(1D / 6D)),
|
||||
SEXOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 6)),
|
||||
|
||||
@Desc("Basic, Smooth & Fast Simplex noise. Uses 7 octaves")
|
||||
SEPTOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 7).scale(1D / 12D)),
|
||||
SEPTOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 7)),
|
||||
|
||||
@Desc("Basic, Smooth & Fast Simplex noise. Uses 8 octaves")
|
||||
OCTOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 8).scale(1D / 25D)),
|
||||
OCTOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 8)),
|
||||
|
||||
@Desc("Basic, Smooth & Fast Simplex noise. Uses 9 octaves")
|
||||
NONOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 9).scale(1D / 50D)),
|
||||
NONOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 9)),
|
||||
|
||||
@Desc("Basic, Smooth & Fast Simplex noise. Uses 10 octaves")
|
||||
VIGOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 10).scale(1D / 100D)),
|
||||
VIGOCTAVE_SIMPLEX(rng -> new CNG(rng, 1D, 10)),
|
||||
|
||||
@Desc("Glob noise is like cellular, but with globs...")
|
||||
GLOB(rng -> new CNG(rng, NoiseType.GLOB, 1D, 1)),
|
||||
GLOB(rng -> new CNG(rng, NoiseType.GLOB, 1D, 1), NoiseType.GLOB),
|
||||
|
||||
@Desc("Glob noise is like cellular, but with globs...")
|
||||
GLOB_IRIS(rng -> CNG.signature(rng, NoiseType.GLOB)),
|
||||
GLOB_IRIS(rng -> CNG.signature(rng, NoiseType.GLOB), NoiseType.GLOB),
|
||||
|
||||
@Desc("Glob noise is like cellular, but with globs...")
|
||||
GLOB_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.GLOB)),
|
||||
GLOB_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.GLOB), NoiseType.GLOB),
|
||||
|
||||
@Desc("Glob noise is like cellular, but with globs...")
|
||||
GLOB_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.GLOB)),
|
||||
GLOB_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.GLOB), NoiseType.GLOB),
|
||||
|
||||
@Desc("Glob noise is like cellular, but with globs...")
|
||||
GLOB_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.GLOB)),
|
||||
GLOB_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.GLOB), NoiseType.GLOB),
|
||||
|
||||
@Desc("Cubic Noise")
|
||||
CUBIC(rng -> new CNG(rng, NoiseType.CUBIC, 1D, 1).scale(256)),
|
||||
CUBIC(rng -> new CNG(rng, NoiseType.CUBIC, 1D, 1), NoiseType.CUBIC),
|
||||
|
||||
@Desc("Fractal Cubic Noise")
|
||||
FRACTAL_CUBIC(rng -> new CNG(rng, NoiseType.FRACTAL_CUBIC, 1D, 1).scale(2)),
|
||||
FRACTAL_CUBIC(rng -> new CNG(rng, NoiseType.FRACTAL_CUBIC, 1D, 1), NoiseType.FRACTAL_CUBIC),
|
||||
|
||||
@Desc("Fractal Cubic Noise With Iris Swirls")
|
||||
FRACTAL_CUBIC_IRIS(rng -> CNG.signature(rng, NoiseType.FRACTAL_CUBIC).scale(2)),
|
||||
FRACTAL_CUBIC_IRIS(rng -> CNG.signature(rng, NoiseType.FRACTAL_CUBIC), NoiseType.FRACTAL_CUBIC),
|
||||
|
||||
@Desc("Fractal Cubic Noise With Iris Swirls")
|
||||
FRACTAL_CUBIC_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.FRACTAL_CUBIC).scale(2)),
|
||||
FRACTAL_CUBIC_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.FRACTAL_CUBIC), NoiseType.FRACTAL_CUBIC),
|
||||
|
||||
@Desc("Fractal Cubic Noise With Iris Swirls")
|
||||
FRACTAL_CUBIC_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.FRACTAL_CUBIC).scale(2)),
|
||||
FRACTAL_CUBIC_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.FRACTAL_CUBIC), NoiseType.FRACTAL_CUBIC),
|
||||
|
||||
@Desc("Fractal Cubic Noise With Iris Swirls")
|
||||
FRACTAL_CUBIC_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.FRACTAL_CUBIC).scale(2)),
|
||||
FRACTAL_CUBIC_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.FRACTAL_CUBIC), NoiseType.FRACTAL_CUBIC),
|
||||
|
||||
@Desc("Fractal Cubic Noise, 2 Octaves")
|
||||
BIOCTAVE_FRACTAL_CUBIC(rng -> new CNG(rng, NoiseType.FRACTAL_CUBIC, 1D, 2).scale(2)),
|
||||
BIOCTAVE_FRACTAL_CUBIC(rng -> new CNG(rng, NoiseType.FRACTAL_CUBIC, 1D, 2), NoiseType.FRACTAL_CUBIC),
|
||||
|
||||
@Desc("Fractal Cubic Noise, 3 Octaves")
|
||||
TRIOCTAVE_FRACTAL_CUBIC(rng -> new CNG(rng, NoiseType.FRACTAL_CUBIC, 1D, 3).scale(1.5)),
|
||||
TRIOCTAVE_FRACTAL_CUBIC(rng -> new CNG(rng, NoiseType.FRACTAL_CUBIC, 1D, 3), NoiseType.FRACTAL_CUBIC),
|
||||
|
||||
@Desc("Fractal Cubic Noise, 4 Octaves")
|
||||
QUADOCTAVE_FRACTAL_CUBIC(rng -> new CNG(rng, NoiseType.FRACTAL_CUBIC, 1D, 4).scale(1)),
|
||||
QUADOCTAVE_FRACTAL_CUBIC(rng -> new CNG(rng, NoiseType.FRACTAL_CUBIC, 1D, 4), NoiseType.FRACTAL_CUBIC),
|
||||
|
||||
@Desc("Cubic Noise")
|
||||
CUBIC_IRIS(rng -> CNG.signature(rng, NoiseType.CUBIC).scale(256)),
|
||||
CUBIC_IRIS(rng -> CNG.signature(rng, NoiseType.CUBIC), NoiseType.CUBIC),
|
||||
|
||||
@Desc("Cubic Noise")
|
||||
CUBIC_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.CUBIC).scale(256)),
|
||||
CUBIC_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.CUBIC), NoiseType.CUBIC),
|
||||
|
||||
@Desc("Cubic Noise")
|
||||
CUBIC_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.CUBIC).scale(256)),
|
||||
CUBIC_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.CUBIC), NoiseType.CUBIC),
|
||||
|
||||
@Desc("Cubic Noise")
|
||||
CUBIC_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.CUBIC).scale(256)),
|
||||
CUBIC_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.CUBIC), NoiseType.CUBIC),
|
||||
|
||||
@Desc("Hexagonal cell noise distorted using Iris styled wispy noise.")
|
||||
HEXAGON_IRIS(rng -> CNG.signature(rng, NoiseType.HEXAGON).scale(1)),
|
||||
HEXAGON_IRIS(rng -> CNG.signature(rng, NoiseType.HEXAGON), NoiseType.HEXAGON),
|
||||
|
||||
@Desc("Hexagonal cell noise distorted using Iris styled wispy noise.")
|
||||
HEXAGON_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.HEXAGON).scale(1)),
|
||||
HEXAGON_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.HEXAGON), NoiseType.HEXAGON),
|
||||
|
||||
@Desc("Hexagonal cell noise distorted using Iris styled wispy noise.")
|
||||
HEXAGON_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.HEXAGON).scale(1)),
|
||||
HEXAGON_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.HEXAGON), NoiseType.HEXAGON),
|
||||
|
||||
@Desc("Hexagonal cell noise distorted using Iris styled wispy noise.")
|
||||
HEXAGON_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.HEXAGON).scale(1)),
|
||||
HEXAGON_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.HEXAGON), NoiseType.HEXAGON),
|
||||
|
||||
@Desc("Hex James substitution pattern distorted using Iris styled wispy noise.")
|
||||
HEX_JAMES_IRIS(rng -> CNG.signature(rng, NoiseType.HEX_JAMES).scale(1)),
|
||||
HEX_JAMES_IRIS(rng -> CNG.signature(rng, NoiseType.HEX_JAMES), NoiseType.HEX_JAMES),
|
||||
|
||||
@Desc("Hex James substitution pattern distorted using Iris styled wispy noise.")
|
||||
HEX_JAMES_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.HEX_JAMES).scale(1)),
|
||||
HEX_JAMES_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.HEX_JAMES), NoiseType.HEX_JAMES),
|
||||
|
||||
@Desc("Hex James substitution pattern distorted using Iris styled wispy noise.")
|
||||
HEX_JAMES_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.HEX_JAMES).scale(1)),
|
||||
HEX_JAMES_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.HEX_JAMES), NoiseType.HEX_JAMES),
|
||||
|
||||
@Desc("Hex James substitution pattern distorted using Iris styled wispy noise.")
|
||||
HEX_JAMES_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.HEX_JAMES).scale(1)),
|
||||
HEX_JAMES_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.HEX_JAMES), NoiseType.HEX_JAMES),
|
||||
|
||||
@Desc("Interlocked solid hex-cell simplex heatmap distorted using Iris styled wispy noise.")
|
||||
HEX_SIMPLEX_IRIS(rng -> CNG.signature(rng, NoiseType.HEX_SIMPLEX).scale(1)),
|
||||
HEX_SIMPLEX_IRIS(rng -> CNG.signature(rng, NoiseType.HEX_SIMPLEX), NoiseType.HEX_SIMPLEX),
|
||||
|
||||
@Desc("Interlocked solid hex-cell simplex heatmap distorted using Iris styled wispy noise.")
|
||||
HEX_SIMPLEX_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.HEX_SIMPLEX).scale(1)),
|
||||
HEX_SIMPLEX_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.HEX_SIMPLEX), NoiseType.HEX_SIMPLEX),
|
||||
|
||||
@Desc("Interlocked solid hex-cell simplex heatmap distorted using Iris styled wispy noise.")
|
||||
HEX_SIMPLEX_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.HEX_SIMPLEX).scale(1)),
|
||||
HEX_SIMPLEX_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.HEX_SIMPLEX), NoiseType.HEX_SIMPLEX),
|
||||
|
||||
@Desc("Interlocked solid hex-cell simplex heatmap distorted using Iris styled wispy noise.")
|
||||
HEX_SIMPLEX_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.HEX_SIMPLEX).scale(1)),
|
||||
HEX_SIMPLEX_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.HEX_SIMPLEX), NoiseType.HEX_SIMPLEX),
|
||||
|
||||
@Desc("Hexagonal random-size gradient noise and distorted using Iris styled wispy noise.")
|
||||
HEX_RANDOM_SIZE_IRIS(rng -> CNG.signature(rng, NoiseType.HEX_RANDOM_SIZE).scale(1)),
|
||||
HEX_RANDOM_SIZE_IRIS(rng -> CNG.signature(rng, NoiseType.HEX_RANDOM_SIZE), NoiseType.HEX_RANDOM_SIZE),
|
||||
|
||||
@Desc("Hexagonal random-size gradient noise and distorted using Iris styled wispy noise.")
|
||||
HEX_RANDOM_SIZE_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.HEX_RANDOM_SIZE).scale(1)),
|
||||
HEX_RANDOM_SIZE_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.HEX_RANDOM_SIZE), NoiseType.HEX_RANDOM_SIZE),
|
||||
|
||||
@Desc("Hexagonal random-size gradient noise and distorted using Iris styled wispy noise.")
|
||||
HEX_RANDOM_SIZE_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.HEX_RANDOM_SIZE).scale(1)),
|
||||
HEX_RANDOM_SIZE_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.HEX_RANDOM_SIZE), NoiseType.HEX_RANDOM_SIZE),
|
||||
|
||||
@Desc("Hexagonal random-size gradient noise and distorted using Iris styled wispy noise.")
|
||||
HEX_RANDOM_SIZE_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.HEX_RANDOM_SIZE).scale(1)),
|
||||
HEX_RANDOM_SIZE_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.HEX_RANDOM_SIZE), NoiseType.HEX_RANDOM_SIZE),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders. Cells are distorted using Iris styled wispy noise.")
|
||||
CELLULAR_IRIS(rng -> CNG.signature(rng, NoiseType.CELLULAR)),
|
||||
CELLULAR_IRIS(rng -> CNG.signature(rng, NoiseType.CELLULAR), NoiseType.CELLULAR),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders. Cells are distorted using Iris styled wispy noise.")
|
||||
CELLULAR_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.CELLULAR)),
|
||||
CELLULAR_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.CELLULAR), NoiseType.CELLULAR),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders. Cells are distorted using Iris styled wispy noise.")
|
||||
CELLULAR_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.CELLULAR)),
|
||||
CELLULAR_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.CELLULAR), NoiseType.CELLULAR),
|
||||
|
||||
@Desc("Cellular noise creates the same noise level for cells, changes noise level on cell borders. Cells are distorted using Iris styled wispy noise.")
|
||||
CELLULAR_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.CELLULAR)),
|
||||
CELLULAR_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.CELLULAR), NoiseType.CELLULAR),
|
||||
|
||||
@Desc("Inverse of vascular, height gets to 1.0 as it approaches the center of a cell")
|
||||
CELLULAR_HEIGHT(rng -> new CNG(rng, NoiseType.CELLULAR_HEIGHT, 1D, 1)),
|
||||
CELLULAR_HEIGHT(rng -> new CNG(rng, NoiseType.CELLULAR_HEIGHT, 1D, 1), NoiseType.CELLULAR_HEIGHT),
|
||||
|
||||
@Desc("Inverse of vascular, height gets to 1.0 as it approaches the center of a cell, using the iris style.")
|
||||
CELLULAR_HEIGHT_IRIS(rng -> CNG.signature(rng, NoiseType.CELLULAR_HEIGHT)),
|
||||
CELLULAR_HEIGHT_IRIS(rng -> CNG.signature(rng, NoiseType.CELLULAR_HEIGHT), NoiseType.CELLULAR_HEIGHT),
|
||||
|
||||
@Desc("Inverse of vascular, height gets to 1.0 as it approaches the center of a cell, using the iris style.")
|
||||
CELLULAR_HEIGHT_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.CELLULAR_HEIGHT)),
|
||||
CELLULAR_HEIGHT_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.CELLULAR_HEIGHT), NoiseType.CELLULAR_HEIGHT),
|
||||
|
||||
@Desc("Inverse of vascular, height gets to 1.0 as it approaches the center of a cell, using the iris style.")
|
||||
CELLULAR_HEIGHT_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.CELLULAR_HEIGHT)),
|
||||
CELLULAR_HEIGHT_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.CELLULAR_HEIGHT), NoiseType.CELLULAR_HEIGHT),
|
||||
|
||||
@Desc("Inverse of vascular, height gets to 1.0 as it approaches the center of a cell, using the iris style.")
|
||||
CELLULAR_HEIGHT_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.CELLULAR_HEIGHT)),
|
||||
CELLULAR_HEIGHT_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.CELLULAR_HEIGHT), NoiseType.CELLULAR_HEIGHT),
|
||||
|
||||
@Desc("Vascular noise gets higher as the position nears a cell border. Cells are distorted using Iris styled wispy noise.")
|
||||
VASCULAR_IRIS(rng -> CNG.signature(rng, NoiseType.VASCULAR)),
|
||||
VASCULAR_IRIS(rng -> CNG.signature(rng, NoiseType.VASCULAR), NoiseType.VASCULAR),
|
||||
|
||||
@Desc("Vascular noise gets higher as the position nears a cell border. Cells are distorted using Iris styled wispy noise.")
|
||||
VASCULAR_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.VASCULAR)),
|
||||
VASCULAR_IRIS_DOUBLE(rng -> CNG.signatureDouble(rng, NoiseType.VASCULAR), NoiseType.VASCULAR),
|
||||
|
||||
@Desc("Vascular noise gets higher as the position nears a cell border. Cells are distorted using Iris styled wispy noise.")
|
||||
VASCULAR_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.VASCULAR)),
|
||||
VASCULAR_IRIS_THICK(rng -> CNG.signatureThick(rng, NoiseType.VASCULAR), NoiseType.VASCULAR),
|
||||
|
||||
@Desc("Vascular noise gets higher as the position nears a cell border. Cells are distorted using Iris styled wispy noise.")
|
||||
VASCULAR_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.VASCULAR)),
|
||||
VASCULAR_IRIS_HALF(rng -> CNG.signatureHalf(rng, NoiseType.VASCULAR), NoiseType.VASCULAR),
|
||||
|
||||
@Desc("Warped gyroid sheets form rounded mazes in 2D and interconnected labyrinths in 3D.")
|
||||
GYROID(rng -> new CNG(rng, NoiseType.GYROID, 1D, 1), NoiseType.GYROID),
|
||||
|
||||
@Desc("Fivefold wave interference forms non-repeating stars, rosettes, and crystalline contours.")
|
||||
QUASICRYSTAL(rng -> new CNG(rng, NoiseType.QUASICRYSTAL, 1D, 1), NoiseType.QUASICRYSTAL),
|
||||
|
||||
@Desc("Connected quarter-circle ribbons form tiled loops that twist continuously through height.")
|
||||
TRUCHET(rng -> new CNG(rng, NoiseType.TRUCHET, 1D, 1), NoiseType.TRUCHET),
|
||||
|
||||
@Desc("Scattered bowls with raised rims form crater fields in 2D and hollow shells in 3D.")
|
||||
CRATER(rng -> new CNG(rng, NoiseType.CRATER, 1D, 1), NoiseType.CRATER),
|
||||
|
||||
@Desc("Seeded spiral arms form swirling eddies in 2D and winding funnels in 3D.")
|
||||
VORTEX(rng -> new CNG(rng, NoiseType.VORTEX, 1D, 1), NoiseType.VORTEX),
|
||||
|
||||
@Desc("Seeded crescent dunes with asymmetric slopes and smooth height-dependent drift.")
|
||||
DUNE(rng -> new CNG(rng, NoiseType.DUNE, 1D, 1), NoiseType.DUNE),
|
||||
|
||||
@Desc("Folded sedimentary bands with varying thickness through the volume.")
|
||||
STRATA(rng -> new CNG(rng, NoiseType.STRATA, 1D, 1), NoiseType.STRATA),
|
||||
|
||||
@Desc("Distorted growth rings and knot-like forms with variation through height.")
|
||||
WOOD(rng -> new CNG(rng, NoiseType.WOOD, 1D, 1), NoiseType.WOOD),
|
||||
|
||||
@Desc("Seeded oriented wave packets form a sparse directional ripple texture.")
|
||||
GABOR(rng -> new CNG(rng, NoiseType.GABOR, 1D, 1), NoiseType.GABOR),
|
||||
|
||||
@Desc("Turbulent stone veins separate broad smooth regions.")
|
||||
MARBLE(rng -> new CNG(rng, NoiseType.MARBLE, 1D, 1), NoiseType.MARBLE),
|
||||
|
||||
@Desc("Overlapping scalloped scales form a seeded tiled surface.")
|
||||
SCALES(rng -> new CNG(rng, NoiseType.SCALES, 1D, 1), NoiseType.SCALES),
|
||||
|
||||
@Desc("Standing-wave nodes form plate-like figures that change through height.")
|
||||
CHLADNI(rng -> new CNG(rng, NoiseType.CHLADNI, 1D, 1), NoiseType.CHLADNI),
|
||||
|
||||
@Desc("Mirrored wedge motifs repeat at a local scale with seeded variation.")
|
||||
KALEIDOSCOPE(rng -> new CNG(rng, NoiseType.KALEIDOSCOPE, 1D, 1), NoiseType.KALEIDOSCOPE),
|
||||
|
||||
@Desc("Recursive cubic voids form a three-dimensional sponge with square-hole slices.")
|
||||
MENGER_SPONGE(rng -> new CNG(rng, NoiseType.MENGER_SPONGE, 1D, 1), NoiseType.MENGER_SPONGE),
|
||||
|
||||
@Desc("Orthogonal tracks and ring pads form connected circuits that shift through height.")
|
||||
CIRCUIT(rng -> new CNG(rng, NoiseType.CIRCUIT, 1D, 1), NoiseType.CIRCUIT),
|
||||
;
|
||||
|
||||
private final CNGFactory f;
|
||||
private final NoiseType type;
|
||||
|
||||
NoiseStyle(CNGFactory f) {
|
||||
this(f, NoiseType.SIMPLEX);
|
||||
}
|
||||
|
||||
NoiseStyle(CNGFactory f, NoiseType type) {
|
||||
this.f = f;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public ProceduralStream<Double> stream(RNG seed) {
|
||||
@@ -555,7 +606,7 @@ public enum NoiseStyle {
|
||||
}
|
||||
|
||||
public CNG create(RNG seed) {
|
||||
CNG cng = f.create(seed).bake();
|
||||
CNG cng = f.create(seed).bake().scale(type.getCoordinateScale()).bake();
|
||||
cng.setLeakStyle(this);
|
||||
return cng;
|
||||
}
|
||||
|
||||
+147
-117
@@ -19,13 +19,13 @@
|
||||
package art.arcane.iris.util.project.interpolation;
|
||||
|
||||
import com.google.common.util.concurrent.AtomicDouble;
|
||||
import art.arcane.volmlib.util.interpolation.Starcast;
|
||||
import art.arcane.iris.engine.object.NoiseStyle;
|
||||
import art.arcane.volmlib.util.function.NoiseProvider;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import art.arcane.iris.util.project.noise.CNG;
|
||||
|
||||
public class IrisInterpolation {
|
||||
private static final double[][] STARCAST_OFFSETS = createStarcastOffsets();
|
||||
public static CNG cng = NoiseStyle.SIMPLEX.create(new RNG());
|
||||
private static final ThreadLocal<NoiseSampleCache2D> NOISE_SAMPLE_CACHE_2D = ThreadLocal.withInitial(() -> new NoiseSampleCache2D(64));
|
||||
private static final ThreadLocal<NoiseBoundsSampleCache2D> NOISE_BOUNDS_SAMPLE_CACHE_2D = ThreadLocal.withInitial(() -> new NoiseBoundsSampleCache2D(64));
|
||||
@@ -35,8 +35,8 @@ public class IrisInterpolation {
|
||||
}
|
||||
|
||||
public static double parametric(double t, double alpha) {
|
||||
double sqt = Math.pow(t, alpha);
|
||||
return sqt / (alpha * (sqt - Math.pow(t, alpha - 1)) + 1.0d);
|
||||
double forward = Math.pow(t, alpha);
|
||||
return forward / (forward + Math.pow(1D - t, alpha));
|
||||
}
|
||||
|
||||
public static float lerpf(float a, float b, float f) {
|
||||
@@ -287,13 +287,17 @@ public class IrisInterpolation {
|
||||
return (int) Math.floor(coord / radius);
|
||||
}
|
||||
|
||||
public static double getBilinearNoise(int x, int z, double rad, NoiseProvider n) {
|
||||
int fx = getRadiusFactor(x, rad);
|
||||
int fz = getRadiusFactor(z, rad);
|
||||
int x1 = (int) Math.round(fx * rad);
|
||||
int z1 = (int) Math.round(fz * rad);
|
||||
int x2 = (int) Math.round((fx + 1) * rad);
|
||||
int z2 = (int) Math.round((fz + 1) * rad);
|
||||
public static int getRadiusFactor(double coord, double radius) {
|
||||
return (int) Math.floor(coord / radius);
|
||||
}
|
||||
|
||||
public static double getBilinearNoise(double x, double z, double rad, NoiseProvider n) {
|
||||
double fx = Math.floor(x / rad);
|
||||
double fz = Math.floor(z / rad);
|
||||
double x1 = fx * rad;
|
||||
double z1 = fz * rad;
|
||||
double x2 = (fx + 1) * rad;
|
||||
double z2 = (fz + 1) * rad;
|
||||
|
||||
double px = rangeScale(0, 1, x1, x2, x);
|
||||
double pz = rangeScale(0, 1, z1, z2, z);
|
||||
@@ -307,13 +311,13 @@ public class IrisInterpolation {
|
||||
//@done
|
||||
}
|
||||
|
||||
public static double getBilinearBezierNoise(int x, int z, double rad, NoiseProvider n) {
|
||||
int fx = getRadiusFactor(x, rad);
|
||||
int fz = getRadiusFactor(z, rad);
|
||||
int x1 = (int) Math.round(fx * rad);
|
||||
int z1 = (int) Math.round(fz * rad);
|
||||
int x2 = (int) Math.round((fx + 1) * rad);
|
||||
int z2 = (int) Math.round((fz + 1) * rad);
|
||||
public static double getBilinearBezierNoise(double x, double z, double rad, NoiseProvider n) {
|
||||
double fx = Math.floor(x / rad);
|
||||
double fz = Math.floor(z / rad);
|
||||
double x1 = fx * rad;
|
||||
double z1 = fz * rad;
|
||||
double x2 = (fx + 1) * rad;
|
||||
double z2 = (fz + 1) * rad;
|
||||
double px = rangeScale(0, 1, x1, x2, x);
|
||||
double pz = rangeScale(0, 1, z1, z2, z);
|
||||
//@builder
|
||||
@@ -326,13 +330,13 @@ public class IrisInterpolation {
|
||||
//@done
|
||||
}
|
||||
|
||||
public static double getBilinearParametricNoise(int x, int z, double rad, NoiseProvider n, double a) {
|
||||
int fx = getRadiusFactor(x, rad);
|
||||
int fz = getRadiusFactor(z, rad);
|
||||
int x1 = (int) Math.round(fx * rad);
|
||||
int z1 = (int) Math.round(fz * rad);
|
||||
int x2 = (int) Math.round((fx + 1) * rad);
|
||||
int z2 = (int) Math.round((fz + 1) * rad);
|
||||
public static double getBilinearParametricNoise(double x, double z, double rad, NoiseProvider n, double a) {
|
||||
double fx = Math.floor(x / rad);
|
||||
double fz = Math.floor(z / rad);
|
||||
double x1 = fx * rad;
|
||||
double z1 = fz * rad;
|
||||
double x2 = (fx + 1) * rad;
|
||||
double z2 = (fz + 1) * rad;
|
||||
double px = rangeScale(0, 1, x1, x2, x);
|
||||
double pz = rangeScale(0, 1, z1, z2, z);
|
||||
//@builder
|
||||
@@ -345,13 +349,13 @@ public class IrisInterpolation {
|
||||
//@done
|
||||
}
|
||||
|
||||
public static double getBilinearCenterSineNoise(int x, int z, double rad, NoiseProvider n) {
|
||||
int fx = getRadiusFactor(x, rad);
|
||||
int fz = getRadiusFactor(z, rad);
|
||||
int x1 = (int) Math.round(fx * rad);
|
||||
int z1 = (int) Math.round(fz * rad);
|
||||
int x2 = (int) Math.round((fx + 1) * rad);
|
||||
int z2 = (int) Math.round((fz + 1) * rad);
|
||||
public static double getBilinearCenterSineNoise(double x, double z, double rad, NoiseProvider n) {
|
||||
double fx = Math.floor(x / rad);
|
||||
double fz = Math.floor(z / rad);
|
||||
double x1 = fx * rad;
|
||||
double z1 = fz * rad;
|
||||
double x2 = (fx + 1) * rad;
|
||||
double z2 = (fz + 1) * rad;
|
||||
double px = rangeScale(0, 1, x1, x2, x);
|
||||
double pz = rangeScale(0, 1, z1, z2, z);
|
||||
//@builder
|
||||
@@ -364,17 +368,17 @@ public class IrisInterpolation {
|
||||
//@done
|
||||
}
|
||||
|
||||
public static double getBicubicNoise(int x, int z, double rad, NoiseProvider n) {
|
||||
int fx = getRadiusFactor(x, rad);
|
||||
int fz = getRadiusFactor(z, rad);
|
||||
int x0 = (int) Math.round((fx - 1) * rad);
|
||||
int z0 = (int) Math.round((fz - 1) * rad);
|
||||
int x1 = (int) Math.round(fx * rad);
|
||||
int z1 = (int) Math.round(fz * rad);
|
||||
int x2 = (int) Math.round((fx + 1) * rad);
|
||||
int z2 = (int) Math.round((fz + 1) * rad);
|
||||
int x3 = (int) Math.round((fx + 2) * rad);
|
||||
int z3 = (int) Math.round((fz + 2) * rad);
|
||||
public static double getBicubicNoise(double x, double z, double rad, NoiseProvider n) {
|
||||
double fx = Math.floor(x / rad);
|
||||
double fz = Math.floor(z / rad);
|
||||
double x0 = (fx - 1) * rad;
|
||||
double z0 = (fz - 1) * rad;
|
||||
double x1 = fx * rad;
|
||||
double z1 = fz * rad;
|
||||
double x2 = (fx + 1) * rad;
|
||||
double z2 = (fz + 1) * rad;
|
||||
double x3 = (fx + 2) * rad;
|
||||
double z3 = (fz + 2) * rad;
|
||||
double px = rangeScale(0, 1, x1, x2, x);
|
||||
double pz = rangeScale(0, 1, z1, z2, z);
|
||||
//@builder
|
||||
@@ -399,17 +403,17 @@ public class IrisInterpolation {
|
||||
//@done
|
||||
}
|
||||
|
||||
public static double getBicubicBezierNoise(int x, int z, double rad, NoiseProvider n) {
|
||||
int fx = getRadiusFactor(x, rad);
|
||||
int fz = getRadiusFactor(z, rad);
|
||||
int x0 = (int) Math.round((fx - 1) * rad);
|
||||
int z0 = (int) Math.round((fz - 1) * rad);
|
||||
int x1 = (int) Math.round(fx * rad);
|
||||
int z1 = (int) Math.round(fz * rad);
|
||||
int x2 = (int) Math.round((fx + 1) * rad);
|
||||
int z2 = (int) Math.round((fz + 1) * rad);
|
||||
int x3 = (int) Math.round((fx + 2) * rad);
|
||||
int z3 = (int) Math.round((fz + 2) * rad);
|
||||
public static double getBicubicBezierNoise(double x, double z, double rad, NoiseProvider n) {
|
||||
double fx = Math.floor(x / rad);
|
||||
double fz = Math.floor(z / rad);
|
||||
double x0 = (fx - 1) * rad;
|
||||
double z0 = (fz - 1) * rad;
|
||||
double x1 = fx * rad;
|
||||
double z1 = fz * rad;
|
||||
double x2 = (fx + 1) * rad;
|
||||
double z2 = (fz + 1) * rad;
|
||||
double x3 = (fx + 2) * rad;
|
||||
double z3 = (fz + 2) * rad;
|
||||
double px = rangeScale(0, 1, x1, x2, x);
|
||||
double pz = rangeScale(0, 1, z1, z2, z);
|
||||
//@builder
|
||||
@@ -434,17 +438,17 @@ public class IrisInterpolation {
|
||||
//@done
|
||||
}
|
||||
|
||||
public static double getBicubicParametricNoise(int x, int z, double rad, NoiseProvider n, double a) {
|
||||
int fx = getRadiusFactor(x, rad);
|
||||
int fz = getRadiusFactor(z, rad);
|
||||
int x0 = (int) Math.round((fx - 1) * rad);
|
||||
int z0 = (int) Math.round((fz - 1) * rad);
|
||||
int x1 = (int) Math.round(fx * rad);
|
||||
int z1 = (int) Math.round(fz * rad);
|
||||
int x2 = (int) Math.round((fx + 1) * rad);
|
||||
int z2 = (int) Math.round((fz + 1) * rad);
|
||||
int x3 = (int) Math.round((fx + 2) * rad);
|
||||
int z3 = (int) Math.round((fz + 2) * rad);
|
||||
public static double getBicubicParametricNoise(double x, double z, double rad, NoiseProvider n, double a) {
|
||||
double fx = Math.floor(x / rad);
|
||||
double fz = Math.floor(z / rad);
|
||||
double x0 = (fx - 1) * rad;
|
||||
double z0 = (fz - 1) * rad;
|
||||
double x1 = fx * rad;
|
||||
double z1 = fz * rad;
|
||||
double x2 = (fx + 1) * rad;
|
||||
double z2 = (fz + 1) * rad;
|
||||
double x3 = (fx + 2) * rad;
|
||||
double z3 = (fz + 2) * rad;
|
||||
double px = rangeScale(0, 1, x1, x2, x);
|
||||
double pz = rangeScale(0, 1, z1, z2, z);
|
||||
//@builder
|
||||
@@ -469,29 +473,29 @@ public class IrisInterpolation {
|
||||
//@done
|
||||
}
|
||||
|
||||
public static double getHermiteNoise(int x, int z, double rad, NoiseProvider n) {
|
||||
public static double getHermiteNoise(double x, double z, double rad, NoiseProvider n) {
|
||||
return getHermiteNoise(x, z, rad, n, 0.5, 0);
|
||||
}
|
||||
|
||||
public static double getHermiteBezierNoise(int x, int z, double rad, NoiseProvider n) {
|
||||
public static double getHermiteBezierNoise(double x, double z, double rad, NoiseProvider n) {
|
||||
return getHermiteBezierNoise(x, z, rad, n, 0.5, 0);
|
||||
}
|
||||
|
||||
public static double getHermiteParametricNoise(int x, int z, double rad, NoiseProvider n, double a) {
|
||||
public static double getHermiteParametricNoise(double x, double z, double rad, NoiseProvider n, double a) {
|
||||
return getHermiteParametricNoise(x, z, rad, n, 0.5, 0, a);
|
||||
}
|
||||
|
||||
public static double getHermiteNoise(int x, int z, double rad, NoiseProvider n, double t, double b) {
|
||||
int fx = getRadiusFactor(x, rad);
|
||||
int fz = getRadiusFactor(z, rad);
|
||||
int x0 = (int) Math.round((fx - 1) * rad);
|
||||
int z0 = (int) Math.round((fz - 1) * rad);
|
||||
int x1 = (int) Math.round(fx * rad);
|
||||
int z1 = (int) Math.round(fz * rad);
|
||||
int x2 = (int) Math.round((fx + 1) * rad);
|
||||
int z2 = (int) Math.round((fz + 1) * rad);
|
||||
int x3 = (int) Math.round((fx + 2) * rad);
|
||||
int z3 = (int) Math.round((fz + 2) * rad);
|
||||
public static double getHermiteNoise(double x, double z, double rad, NoiseProvider n, double t, double b) {
|
||||
double fx = Math.floor(x / rad);
|
||||
double fz = Math.floor(z / rad);
|
||||
double x0 = (fx - 1) * rad;
|
||||
double z0 = (fz - 1) * rad;
|
||||
double x1 = fx * rad;
|
||||
double z1 = fz * rad;
|
||||
double x2 = (fx + 1) * rad;
|
||||
double z2 = (fz + 1) * rad;
|
||||
double x3 = (fx + 2) * rad;
|
||||
double z3 = (fz + 2) * rad;
|
||||
double px = rangeScale(0, 1, x1, x2, x);
|
||||
double pz = rangeScale(0, 1, z1, z2, z);
|
||||
//@builder
|
||||
@@ -516,17 +520,17 @@ public class IrisInterpolation {
|
||||
//@done
|
||||
}
|
||||
|
||||
public static double getHermiteBezierNoise(int x, int z, double rad, NoiseProvider n, double t, double b) {
|
||||
int fx = getRadiusFactor(x, rad);
|
||||
int fz = getRadiusFactor(z, rad);
|
||||
int x0 = (int) Math.round((fx - 1) * rad);
|
||||
int z0 = (int) Math.round((fz - 1) * rad);
|
||||
int x1 = (int) Math.round(fx * rad);
|
||||
int z1 = (int) Math.round(fz * rad);
|
||||
int x2 = (int) Math.round((fx + 1) * rad);
|
||||
int z2 = (int) Math.round((fz + 1) * rad);
|
||||
int x3 = (int) Math.round((fx + 2) * rad);
|
||||
int z3 = (int) Math.round((fz + 2) * rad);
|
||||
public static double getHermiteBezierNoise(double x, double z, double rad, NoiseProvider n, double t, double b) {
|
||||
double fx = Math.floor(x / rad);
|
||||
double fz = Math.floor(z / rad);
|
||||
double x0 = (fx - 1) * rad;
|
||||
double z0 = (fz - 1) * rad;
|
||||
double x1 = fx * rad;
|
||||
double z1 = fz * rad;
|
||||
double x2 = (fx + 1) * rad;
|
||||
double z2 = (fz + 1) * rad;
|
||||
double x3 = (fx + 2) * rad;
|
||||
double z3 = (fz + 2) * rad;
|
||||
double px = rangeScale(0, 1, x1, x2, x);
|
||||
double pz = rangeScale(0, 1, z1, z2, z);
|
||||
//@builder
|
||||
@@ -551,17 +555,17 @@ public class IrisInterpolation {
|
||||
//@done
|
||||
}
|
||||
|
||||
public static double getHermiteParametricNoise(int x, int z, double rad, NoiseProvider n, double t, double b, double a) {
|
||||
int fx = getRadiusFactor(x, rad);
|
||||
int fz = getRadiusFactor(z, rad);
|
||||
int x0 = (int) Math.round((fx - 1) * rad);
|
||||
int z0 = (int) Math.round((fz - 1) * rad);
|
||||
int x1 = (int) Math.round(fx * rad);
|
||||
int z1 = (int) Math.round(fz * rad);
|
||||
int x2 = (int) Math.round((fx + 1) * rad);
|
||||
int z2 = (int) Math.round((fz + 1) * rad);
|
||||
int x3 = (int) Math.round((fx + 2) * rad);
|
||||
int z3 = (int) Math.round((fz + 2) * rad);
|
||||
public static double getHermiteParametricNoise(double x, double z, double rad, NoiseProvider n, double t, double b, double a) {
|
||||
double fx = Math.floor(x / rad);
|
||||
double fz = Math.floor(z / rad);
|
||||
double x0 = (fx - 1) * rad;
|
||||
double z0 = (fz - 1) * rad;
|
||||
double x1 = fx * rad;
|
||||
double z1 = fz * rad;
|
||||
double x2 = (fx + 1) * rad;
|
||||
double z2 = (fz + 1) * rad;
|
||||
double x3 = (fx + 2) * rad;
|
||||
double z3 = (fz + 2) * rad;
|
||||
double px = rangeScale(0, 1, x1, x2, x);
|
||||
double pz = rangeScale(0, 1, z1, z2, z);
|
||||
//@builder
|
||||
@@ -601,7 +605,7 @@ public class IrisInterpolation {
|
||||
return rad.get();
|
||||
}
|
||||
|
||||
public static double getNoise(InterpolationMethod method, int x, int z, double h, NoiseProvider noise) {
|
||||
public static double getNoise(InterpolationMethod method, double x, double z, double h, NoiseProvider noise) {
|
||||
if (usesSampleCache(method)) {
|
||||
NoiseSampleCache2D cache = NOISE_SAMPLE_CACHE_2D.get();
|
||||
if (!cache.isInUse()) {
|
||||
@@ -620,37 +624,37 @@ public class IrisInterpolation {
|
||||
return dispatch(method, x, z, h, noise);
|
||||
}
|
||||
|
||||
private static double dispatch(InterpolationMethod method, int x, int z, double h, NoiseProvider n) {
|
||||
private static double dispatch(InterpolationMethod method, double x, double z, double h, NoiseProvider n) {
|
||||
return switch (method) {
|
||||
case BILINEAR -> getBilinearNoise(x, z, h, n);
|
||||
case STARCAST_3 -> Starcast.starcast(x, z, h, 3D, n);
|
||||
case STARCAST_6 -> Starcast.starcast(x, z, h, 6D, n);
|
||||
case STARCAST_9 -> Starcast.starcast(x, z, h, 9D, n);
|
||||
case STARCAST_12 -> Starcast.starcast(x, z, h, 12D, n);
|
||||
case STARCAST_3 -> starcast(x, z, h, 3, n);
|
||||
case STARCAST_6 -> starcast(x, z, h, 6, n);
|
||||
case STARCAST_9 -> starcast(x, z, h, 9, n);
|
||||
case STARCAST_12 -> starcast(x, z, h, 12, n);
|
||||
case BILINEAR_STARCAST_3 ->
|
||||
Starcast.starcast(x, z, h, 3D, (xx, zz) -> getBilinearNoise((int) xx, (int) zz, h, n));
|
||||
starcast(x, z, h, 3, (xx, zz) -> getBilinearNoise(xx, zz, h, n));
|
||||
case BILINEAR_STARCAST_6 ->
|
||||
Starcast.starcast(x, z, h, 6D, (xx, zz) -> getBilinearNoise((int) xx, (int) zz, h, n));
|
||||
starcast(x, z, h, 6, (xx, zz) -> getBilinearNoise(xx, zz, h, n));
|
||||
case BILINEAR_STARCAST_9 ->
|
||||
Starcast.starcast(x, z, h, 9D, (xx, zz) -> getBilinearNoise((int) xx, (int) zz, h, n));
|
||||
starcast(x, z, h, 9, (xx, zz) -> getBilinearNoise(xx, zz, h, n));
|
||||
case BILINEAR_STARCAST_12 ->
|
||||
Starcast.starcast(x, z, h, 12D, (xx, zz) -> getBilinearNoise((int) xx, (int) zz, h, n));
|
||||
starcast(x, z, h, 12, (xx, zz) -> getBilinearNoise(xx, zz, h, n));
|
||||
case HERMITE_STARCAST_3 ->
|
||||
Starcast.starcast(x, z, h, 3D, (xx, zz) -> getHermiteNoise((int) xx, (int) zz, h, n, 0D, 0D));
|
||||
starcast(x, z, h, 3, (xx, zz) -> getHermiteNoise(xx, zz, h, n, 0D, 0D));
|
||||
case HERMITE_STARCAST_6 ->
|
||||
Starcast.starcast(x, z, h, 6D, (xx, zz) -> getHermiteNoise((int) xx, (int) zz, h, n, 0D, 0D));
|
||||
starcast(x, z, h, 6, (xx, zz) -> getHermiteNoise(xx, zz, h, n, 0D, 0D));
|
||||
case HERMITE_STARCAST_9 ->
|
||||
Starcast.starcast(x, z, h, 9D, (xx, zz) -> getHermiteNoise((int) xx, (int) zz, h, n, 0D, 0D));
|
||||
starcast(x, z, h, 9, (xx, zz) -> getHermiteNoise(xx, zz, h, n, 0D, 0D));
|
||||
case HERMITE_STARCAST_12 ->
|
||||
Starcast.starcast(x, z, h, 12D, (xx, zz) -> getHermiteNoise((int) xx, (int) zz, h, n, 0D, 0D));
|
||||
starcast(x, z, h, 12, (xx, zz) -> getHermiteNoise(xx, zz, h, n, 0D, 0D));
|
||||
case BILINEAR_BEZIER -> getBilinearBezierNoise(x, z, h, n);
|
||||
case BILINEAR_PARAMETRIC_2 -> getBilinearParametricNoise(x, z, h, n, 2);
|
||||
case BILINEAR_PARAMETRIC_4 -> getBilinearParametricNoise(x, z, h, n, 4);
|
||||
case BILINEAR_PARAMETRIC_1_5 -> getBilinearParametricNoise(x, z, h, n, 1.5);
|
||||
case BICUBIC -> getBilinearNoise(x, z, h, n);
|
||||
case BICUBIC -> getBicubicNoise(x, z, h, n);
|
||||
case HERMITE -> getHermiteNoise(x, z, h, n);
|
||||
case HERMITE_TENSE -> getHermiteNoise(x, z, h, n, 0.8D, 0D);
|
||||
case CATMULL_ROM_SPLINE -> getHermiteNoise(x, z, h, n, 1D, 0D);
|
||||
case CATMULL_ROM_SPLINE -> getHermiteNoise(x, z, h, n, 0D, 0D);
|
||||
case HERMITE_LOOSE -> getHermiteNoise(x, z, h, n, 0D, 0D);
|
||||
case HERMITE_LOOSE_HALF_NEGATIVE_BIAS -> getHermiteNoise(x, z, h, n, 0D, -0.5D);
|
||||
case HERMITE_LOOSE_HALF_POSITIVE_BIAS -> getHermiteNoise(x, z, h, n, 0D, 0.5D);
|
||||
@@ -660,7 +664,7 @@ public class IrisInterpolation {
|
||||
};
|
||||
}
|
||||
|
||||
public static NoiseBounds getNoiseBounds(InterpolationMethod method, int x, int z, double h, NoiseBoundsProvider noise) {
|
||||
public static NoiseBounds getNoiseBounds(InterpolationMethod method, double x, double z, double h, NoiseBoundsProvider noise) {
|
||||
NoiseBoundsSampleCache2D cache = NOISE_BOUNDS_SAMPLE_CACHE_2D.get();
|
||||
if (cache.isInUse()) {
|
||||
// Nested bounds interpolation: a fresh table on the rare nested path, never the
|
||||
@@ -687,6 +691,32 @@ public class IrisInterpolation {
|
||||
}
|
||||
}
|
||||
|
||||
private static double[][] createStarcastOffsets() {
|
||||
double[][] offsets = new double[4][];
|
||||
for (int index = 0; index < offsets.length; index++) {
|
||||
int checks = (index + 1) * 3;
|
||||
double[] samples = new double[checks * 2];
|
||||
for (int sample = 0; sample < checks; sample++) {
|
||||
double angle = sample * (Math.PI * 2D / checks);
|
||||
double sin = Math.sin(angle);
|
||||
double cos = Math.cos(angle);
|
||||
samples[sample * 2] = cos - sin;
|
||||
samples[sample * 2 + 1] = sin + cos;
|
||||
}
|
||||
offsets[index] = samples;
|
||||
}
|
||||
return offsets;
|
||||
}
|
||||
|
||||
private static double starcast(double x, double z, double radius, int checks, NoiseProvider noise) {
|
||||
double[] offsets = STARCAST_OFFSETS[checks / 3 - 1];
|
||||
double sum = 0D;
|
||||
for (int sample = 0; sample < offsets.length; sample += 2) {
|
||||
sum += noise.noise(x + radius * offsets[sample], z + radius * offsets[sample + 1]);
|
||||
}
|
||||
return sum / checks;
|
||||
}
|
||||
|
||||
private static boolean usesSampleCache(InterpolationMethod method) {
|
||||
return switch (method) {
|
||||
case BILINEAR_STARCAST_3,
|
||||
|
||||
@@ -39,6 +39,7 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@@ -79,6 +80,7 @@ public class CNG {
|
||||
private transient double signedFractureScale;
|
||||
private transient boolean fastPathStateDirty = true;
|
||||
private transient int coordCacheSalt;
|
||||
private transient Object coordCacheIdentity = new Object();
|
||||
|
||||
public CNG(RNG random) {
|
||||
this(random, 1);
|
||||
@@ -93,7 +95,7 @@ public class CNG {
|
||||
}
|
||||
|
||||
public CNG(RNG random, NoiseType type, double opacity, int octaves) {
|
||||
this(random, type.create(random.nextParallelRNG((long) ((1113334944L * opacity) + 12922 + octaves)).lmax()), opacity, octaves);
|
||||
this(random, type.create(random.nextParallelRNG((long) ((1113334944L * opacity) + 12923)).lmax()), opacity, octaves);
|
||||
}
|
||||
|
||||
public CNG(RNG random, NoiseGenerator generator, double opacity, int octaves) {
|
||||
@@ -115,8 +117,8 @@ public class CNG {
|
||||
this.injectorMode = InjectorMode.ADD;
|
||||
coordCacheSalt = createCoordCacheSalt();
|
||||
|
||||
if (generator instanceof OctaveNoise) {
|
||||
((OctaveNoise) generator).setOctaves(octaves);
|
||||
if (generator instanceof OctaveNoise octaveNoise) {
|
||||
octaveNoise.setOctaves(octaves);
|
||||
}
|
||||
|
||||
refreshFastPathState();
|
||||
@@ -256,7 +258,7 @@ public class CNG {
|
||||
return this;
|
||||
}
|
||||
|
||||
cache = null;
|
||||
markFastPathStateDirty();
|
||||
|
||||
File f = new File(new File(cacheFolder, ".cache"), key + ".cnm");
|
||||
FloatCache fbc;
|
||||
@@ -306,6 +308,7 @@ public class CNG {
|
||||
}
|
||||
|
||||
cache = fbc;
|
||||
coordCacheIdentity = new Object();
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -355,6 +358,16 @@ public class CNG {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CNG zoom(double factor) {
|
||||
if (!Double.isFinite(factor) || factor <= 0D || !Double.isFinite(1D / factor)) {
|
||||
throw new IllegalArgumentException("Zoom must be positive and finite");
|
||||
}
|
||||
if (factor != 1D) {
|
||||
zoom(factor, new IdentityHashMap<>());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CNG patch(double c) {
|
||||
patch = c;
|
||||
markFastPathStateDirty();
|
||||
@@ -650,46 +663,16 @@ public class CNG {
|
||||
return IrisInterpolation.lerp(min, max, noise);
|
||||
}
|
||||
|
||||
private double getNoise(double... dim) {
|
||||
ensureFastPathState();
|
||||
double scale = effectiveScale;
|
||||
|
||||
if (fracture == null || noscale) {
|
||||
return generator.noise(
|
||||
(dim.length > 0 ? dim[0] : 0D) * scale,
|
||||
(dim.length > 1 ? dim[1] : 0D) * scale,
|
||||
(dim.length > 2 ? dim[2] : 0D) * scale) * opacity;
|
||||
}
|
||||
|
||||
double x = dim.length > 0 ? dim[0] : 0D;
|
||||
double y = dim.length > 1 ? dim[1] : 0D;
|
||||
double z = dim.length > 2 ? dim[2] : 0D;
|
||||
|
||||
if (dim.length > 0) {
|
||||
x += (fracture.noise(dim) - 0.5) * fscale;
|
||||
}
|
||||
|
||||
if (dim.length > 1) {
|
||||
y += (fracture.noise(dim[1], dim[0]) - 0.5) * fscale;
|
||||
}
|
||||
|
||||
if (dim.length > 2) {
|
||||
z += (fracture.noise(dim[2], dim[0], dim[1]) - 0.5) * fscale;
|
||||
}
|
||||
|
||||
return generator.noise(x * scale, y * scale, z * scale) * opacity;
|
||||
}
|
||||
|
||||
private double getNoise(double x) {
|
||||
ensureFastPathState();
|
||||
double scl = effectiveScale;
|
||||
|
||||
if (fracture == null || noscale) {
|
||||
return generator.noise(x * scl, 0D, 0D) * opacity;
|
||||
return generator.noise(x * scl) * opacity;
|
||||
}
|
||||
|
||||
double fx = x + ((fracture.noiseFast1D(x) - 0.5D) * fscale);
|
||||
return generator.noise(fx * scl, 0D, 0D) * opacity;
|
||||
return generator.noise(fx * scl) * opacity;
|
||||
}
|
||||
|
||||
private double getNoise(double x, double z) {
|
||||
@@ -697,12 +680,12 @@ public class CNG {
|
||||
double scl = effectiveScale;
|
||||
|
||||
if (fracture == null || noscale) {
|
||||
return generator.noise(x * scl, z * scl, 0D) * opacity;
|
||||
return generator.noise(x * scl, z * scl) * opacity;
|
||||
}
|
||||
|
||||
double fx = x + ((fracture.noiseFast2D(x, z) - 0.5D) * fscale);
|
||||
double fz = z + ((fracture.noiseFast2D(z, x) - 0.5D) * fscale);
|
||||
return generator.noise(fx * scl, fz * scl, 0D) * opacity;
|
||||
return generator.noise(fx * scl, fz * scl) * opacity;
|
||||
}
|
||||
|
||||
private double getNoise(double x, double y, double z) {
|
||||
@@ -735,8 +718,15 @@ public class CNG {
|
||||
return (noise(dim) * 2) - 1;
|
||||
}
|
||||
|
||||
private static boolean isWholeCoordinate(double value) {
|
||||
return value == Math.rint(value);
|
||||
private boolean isCachedCoordinate(double x, double z) {
|
||||
return cache != null
|
||||
&& x >= 0D && x < cache.getWidth()
|
||||
&& z >= 0D && z < cache.getHeight()
|
||||
&& x == (int) x && z == (int) z;
|
||||
}
|
||||
|
||||
private double getCachedNoise(double x, double z) {
|
||||
return cache.get((int) z * cache.getWidth() + (int) x);
|
||||
}
|
||||
|
||||
private double applyPost(double n, double x) {
|
||||
@@ -860,57 +850,12 @@ public class CNG {
|
||||
}
|
||||
|
||||
public double noise(double... dim) {
|
||||
if (dim.length == 1) {
|
||||
return noise(dim[0]);
|
||||
}
|
||||
|
||||
if (dim.length == 2) {
|
||||
return noise(dim[0], dim[1]);
|
||||
}
|
||||
|
||||
if (dim.length == 3) {
|
||||
return noise(dim[0], dim[1], dim[2]);
|
||||
}
|
||||
|
||||
double n = getNoise(dim);
|
||||
n = power != 1D ? (n < 0 ? -Math.pow(Math.abs(n), power) : Math.pow(n, power)) : n;
|
||||
double m = 1;
|
||||
if (children == null) {
|
||||
return (n - down + up) * patch;
|
||||
}
|
||||
|
||||
for (CNG i : children) {
|
||||
double source = n;
|
||||
double value = i.noise(dim);
|
||||
switch (injectorMode) {
|
||||
case ADD -> {
|
||||
n = source + value;
|
||||
m += 1D;
|
||||
}
|
||||
case SRC_SUBTRACT -> {
|
||||
n = source - value < 0D ? 0D : source - value;
|
||||
m -= 1D;
|
||||
}
|
||||
case DST_SUBTRACT -> {
|
||||
n = value - source < 0D ? 0D : source - value;
|
||||
m -= 1D;
|
||||
}
|
||||
case MULTIPLY -> n = source * value;
|
||||
case MAX -> n = Math.max(source, value);
|
||||
case MIN -> n = Math.min(source, value);
|
||||
case SRC_MOD -> n = source % value;
|
||||
case SRC_POW -> n = Math.pow(source, value);
|
||||
case DST_MOD -> n = value % source;
|
||||
case DST_POW -> n = Math.pow(value, source);
|
||||
case CUSTOM -> {
|
||||
double[] combined = injector.combine(source, value);
|
||||
n = combined[0];
|
||||
m += combined[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ((n / m) - down + up) * patch;
|
||||
return switch (dim.length) {
|
||||
case 1 -> noise(dim[0]);
|
||||
case 2 -> noise(dim[0], dim[1]);
|
||||
case 3 -> noise(dim[0], dim[1], dim[2]);
|
||||
default -> throw new IllegalArgumentException("Noise requires one, two, or three coordinates");
|
||||
};
|
||||
}
|
||||
|
||||
public double noise(double x) {
|
||||
@@ -922,16 +867,16 @@ public class CNG {
|
||||
}
|
||||
|
||||
public double noise(double x, double z) {
|
||||
if (cache != null && isWholeCoordinate(x) && isWholeCoordinate(z)) {
|
||||
return cache.get((int) x, (int) z);
|
||||
if (isCachedCoordinate(x, z)) {
|
||||
return getCachedNoise(x, z);
|
||||
}
|
||||
|
||||
return applyPost(getNoise(x, z), x, z);
|
||||
}
|
||||
|
||||
public double noiseFast2D(double x, double z) {
|
||||
if (cache != null && isWholeCoordinate(x) && isWholeCoordinate(z)) {
|
||||
return cache.get((int) x, (int) z);
|
||||
if (isCachedCoordinate(x, z)) {
|
||||
return getCachedNoise(x, z);
|
||||
}
|
||||
|
||||
if (isIdentityPostFastPath()) {
|
||||
@@ -963,12 +908,13 @@ public class CNG {
|
||||
long kx = Double.doubleToRawLongBits(x);
|
||||
long kz = Double.doubleToRawLongBits(z);
|
||||
CoordCache cc = COORD_CACHE_2D.get();
|
||||
Object identity = coordCacheIdentity();
|
||||
int slot = coordSlot(kx, kz, coordCacheSalt());
|
||||
if (cc.owner[slot] == this && cc.kx[slot] == kx && cc.kz[slot] == kz) {
|
||||
if (cc.owner[slot] == identity && cc.kx[slot] == kx && cc.kz[slot] == kz) {
|
||||
return cc.value[slot];
|
||||
}
|
||||
double computed = computeNoiseFastSigned2D(x, z);
|
||||
cc.owner[slot] = this;
|
||||
cc.owner[slot] = identity;
|
||||
cc.kx[slot] = kx;
|
||||
cc.kz[slot] = kz;
|
||||
cc.value[slot] = computed;
|
||||
@@ -976,8 +922,8 @@ public class CNG {
|
||||
}
|
||||
|
||||
private double computeNoiseFastSigned2D(double x, double z) {
|
||||
if (cache != null && isWholeCoordinate(x) && isWholeCoordinate(z)) {
|
||||
return (cache.get((int) x, (int) z) * 2D) - 1D;
|
||||
if (isCachedCoordinate(x, z)) {
|
||||
return (getCachedNoise(x, z) * 2D) - 1D;
|
||||
}
|
||||
|
||||
if (hasIdentitySignedFastPath()) {
|
||||
@@ -1014,12 +960,13 @@ public class CNG {
|
||||
long ky = Double.doubleToRawLongBits(y);
|
||||
long kz = Double.doubleToRawLongBits(z);
|
||||
CoordCache3D cc = COORD_CACHE_3D.get();
|
||||
Object identity = coordCacheIdentity();
|
||||
int slot = coordSlot(kx ^ Long.rotateLeft(ky, 21), kz, coordCacheSalt());
|
||||
if (cc.owner[slot] == this && cc.kx[slot] == kx && cc.ky[slot] == ky && cc.kz[slot] == kz) {
|
||||
if (cc.owner[slot] == identity && cc.kx[slot] == kx && cc.ky[slot] == ky && cc.kz[slot] == kz) {
|
||||
return cc.value[slot];
|
||||
}
|
||||
double computed = hasIdentitySignedFastPath() ? getSignedNoise(x, y, z) : (noiseFast3D(x, y, z) * 2D) - 1D;
|
||||
cc.owner[slot] = this;
|
||||
cc.owner[slot] = identity;
|
||||
cc.kx[slot] = kx;
|
||||
cc.ky[slot] = ky;
|
||||
cc.kz[slot] = kz;
|
||||
@@ -1034,7 +981,15 @@ public class CNG {
|
||||
}
|
||||
|
||||
public CNG oct(int octaves) {
|
||||
if (oct == octaves) {
|
||||
return this;
|
||||
}
|
||||
|
||||
oct = octaves;
|
||||
if (generator instanceof OctaveNoise octaveNoise) {
|
||||
octaveNoise.setOctaves(octaves);
|
||||
}
|
||||
markFastPathStateDirty();
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1056,6 +1011,23 @@ public class CNG {
|
||||
return identitySignedFastPath;
|
||||
}
|
||||
|
||||
private void zoom(double factor, IdentityHashMap<CNG, Boolean> visited) {
|
||||
if (visited.put(this, Boolean.TRUE) != null) {
|
||||
return;
|
||||
}
|
||||
bakedScale /= factor;
|
||||
fscale *= factor;
|
||||
if (fracture != null) {
|
||||
fracture.zoom(factor, visited);
|
||||
}
|
||||
if (children != null) {
|
||||
for (CNG child : children) {
|
||||
child.zoom(factor, visited);
|
||||
}
|
||||
}
|
||||
markFastPathStateDirty();
|
||||
}
|
||||
|
||||
private void ensureFastPathState() {
|
||||
if (fastPathStateDirty) {
|
||||
refreshFastPathState();
|
||||
@@ -1064,6 +1036,16 @@ public class CNG {
|
||||
|
||||
private void markFastPathStateDirty() {
|
||||
fastPathStateDirty = true;
|
||||
cache = null;
|
||||
coordCacheIdentity = new Object();
|
||||
}
|
||||
|
||||
private Object coordCacheIdentity() {
|
||||
if (coordCacheIdentity == null) {
|
||||
coordCacheIdentity = new Object();
|
||||
}
|
||||
|
||||
return coordCacheIdentity;
|
||||
}
|
||||
|
||||
private int coordCacheSalt() {
|
||||
@@ -1112,12 +1094,12 @@ public class CNG {
|
||||
CNG localFracture = fracture;
|
||||
|
||||
if (localFracture == null || noscale) {
|
||||
return signedWithOpacity(localGenerator.noiseSigned(x * scl, z * scl, 0D));
|
||||
return signedWithOpacity(localGenerator.noiseSigned(x * scl, z * scl));
|
||||
}
|
||||
|
||||
double fx = x + (localFracture.noiseFastSigned2D(x, z) * signedFractureScale);
|
||||
double fz = z + (localFracture.noiseFastSigned2D(z, x) * signedFractureScale);
|
||||
return signedWithOpacity(localGenerator.noiseSigned(fx * scl, fz * scl, 0D));
|
||||
return signedWithOpacity(localGenerator.noiseSigned(fx * scl, fz * scl));
|
||||
}
|
||||
|
||||
private double getSignedNoise(double x, double y, double z) {
|
||||
|
||||
@@ -32,7 +32,7 @@ public class CellHeightNoise implements NoiseGenerator {
|
||||
}
|
||||
|
||||
private double filter(double noise) {
|
||||
return M.clip(1D - ((noise / 2D) + 0.5D), 0D, 1D);
|
||||
return M.clip((noise / 2D) + 0.5D, 0D, 1D);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,27 +21,27 @@ package art.arcane.iris.util.project.noise;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
|
||||
public class CellularNoise implements NoiseGenerator {
|
||||
private final FastNoise n;
|
||||
private final FastNoiseDouble n;
|
||||
|
||||
public CellularNoise(long seed) {
|
||||
this.n = new FastNoise(new RNG(seed).imax());
|
||||
n.SetNoiseType(FastNoise.NoiseType.Cellular);
|
||||
n.SetCellularReturnType(FastNoise.CellularReturnType.CellValue);
|
||||
n.SetCellularDistanceFunction(FastNoise.CellularDistanceFunction.Natural);
|
||||
this.n = new FastNoiseDouble(new RNG(seed).lmax());
|
||||
n.setNoiseType(FastNoiseDouble.NoiseType.Cellular);
|
||||
n.setCellularReturnType(FastNoiseDouble.CellularReturnType.CellValue);
|
||||
n.setCellularDistanceFunction(FastNoiseDouble.CellularDistanceFunction.Natural);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x) {
|
||||
return (n.GetCellular((float) x, 0) / 2D) + 0.5D;
|
||||
return (n.GetCellular(x, 0) / 2D) + 0.5D;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double z) {
|
||||
return (n.GetCellular((float) x, (float) z) / 2D) + 0.5D;
|
||||
return (n.GetCellular(x, z) / 2D) + 0.5D;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double y, double z) {
|
||||
return (n.GetCellular((float) x, (float) y, (float) z) / 2D) + 0.5D;
|
||||
return (n.GetCellular(x, y, z) / 2D) + 0.5D;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
public final class ChladniNoise extends PatternNoise {
|
||||
private final int firstMode;
|
||||
private final int secondMode;
|
||||
private final double phase;
|
||||
private final double width;
|
||||
|
||||
public ChladniNoise(long seed) {
|
||||
long hash = mix(seed);
|
||||
firstMode = 2 + (int) (hash & 1L);
|
||||
secondMode = firstMode + 1 + (int) ((hash >>> 1) & 1L);
|
||||
phase = (hash >>> 11) * 0x1.0p-52D;
|
||||
width = 0.22D + ((hash >>> 3) & 255L) * (0.12D / 255D);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
double u = (x + phase) * Math.PI;
|
||||
double v = (z + phase) * Math.PI;
|
||||
double first = Math.cos(firstMode * u) * Math.cos(secondMode * v);
|
||||
double second = Math.cos(secondMode * u) * Math.cos(firstMode * v);
|
||||
double field = first * Math.cos(y * Math.PI * 0.5D)
|
||||
- second * Math.cos(y * Math.PI * 0.75D);
|
||||
double node = Math.max(0D, 1D - Math.abs(field) / width);
|
||||
return node * node * (3D - 2D * node);
|
||||
}
|
||||
|
||||
private static long mix(long value) {
|
||||
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
|
||||
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
|
||||
return value ^ (value >>> 31);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
public final class CircuitNoise extends PatternNoise {
|
||||
private final long seed;
|
||||
private final double phaseX;
|
||||
private final double phaseZ;
|
||||
|
||||
public CircuitNoise(long seed) {
|
||||
this.seed = seed;
|
||||
phaseX = (mix(seed) >>> 11) * 0x1.0p-53;
|
||||
phaseZ = (mix(seed + 0x9E3779B97F4A7C15L) >>> 11) * 0x1.0p-53;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
x += phaseX;
|
||||
z += phaseZ;
|
||||
if (y != 0D) {
|
||||
x += y * 0.13D + 0.17D * Math.sin(y * Math.PI * 0.5D);
|
||||
z += 0.11D * Math.sin(y * Math.PI * 0.75D);
|
||||
}
|
||||
long cellX = (long) Math.floor(x);
|
||||
long cellZ = (long) Math.floor(z);
|
||||
x -= cellX;
|
||||
z -= cellZ;
|
||||
long hash = mix(seed ^ cellX * 0x9E3779B97F4A7C15L ^ cellZ * 0xD1B54A32D192ED03L);
|
||||
double distance;
|
||||
double padDistance;
|
||||
if ((hash & 3L) == 0L) {
|
||||
distance = Math.min(Math.abs(x - 0.5D), Math.abs(z - 0.5D));
|
||||
double dx = x - 0.5D;
|
||||
double dz = z - 0.5D;
|
||||
padDistance = Math.sqrt(dx * dx + dz * dz);
|
||||
} else {
|
||||
if ((hash & 1L) != 0L) {
|
||||
x = 1D - x;
|
||||
}
|
||||
distance = Math.sqrt(Math.min(routeDistanceSquared(x, z), routeDistanceSquared(1D - x, 1D - z)));
|
||||
double dx = x - 0.75D;
|
||||
double dz = z - 0.25D;
|
||||
double oppositeX = x - 0.25D;
|
||||
double oppositeZ = z - 0.75D;
|
||||
padDistance = Math.sqrt(Math.min(dx * dx + dz * dz, oppositeX * oppositeX + oppositeZ * oppositeZ));
|
||||
}
|
||||
double trace = smooth(1D - distance / 0.045D);
|
||||
double pad = smooth(1D - Math.abs(padDistance - 0.085D) / 0.03D);
|
||||
return Math.max(trace, pad) * smooth(padDistance / 0.045D);
|
||||
}
|
||||
|
||||
private static double routeDistanceSquared(double x, double z) {
|
||||
double top = segmentDistanceSquared(x - 0.5D, z, 0D, 0.25D);
|
||||
double bend = segmentDistanceSquared(z - 0.25D, x, 0.5D, 0.75D);
|
||||
double middle = segmentDistanceSquared(x - 0.75D, z, 0.25D, 0.5D);
|
||||
double right = segmentDistanceSquared(z - 0.5D, x, 0.75D, 1D);
|
||||
return Math.min(Math.min(top, bend), Math.min(middle, right));
|
||||
}
|
||||
|
||||
private static double segmentDistanceSquared(double perpendicular, double along, double start, double end) {
|
||||
double excess = Math.max(start - along, Math.max(along - end, 0D));
|
||||
return perpendicular * perpendicular + excess * excess;
|
||||
}
|
||||
|
||||
private static double smooth(double value) {
|
||||
double bounded = Math.clamp(value, 0D, 1D);
|
||||
return bounded * bounded * (3D - 2D * bounded);
|
||||
}
|
||||
|
||||
private static long mix(long value) {
|
||||
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
|
||||
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
|
||||
return value ^ (value >>> 31);
|
||||
}
|
||||
}
|
||||
@@ -39,10 +39,6 @@ public class CloverNoise implements NoiseGenerator {
|
||||
|
||||
@Override
|
||||
public double noise(double x, double y, double z) {
|
||||
if (z == 0) {
|
||||
return n2.noise(x, y);
|
||||
}
|
||||
|
||||
return n3.noise(x, y, z);
|
||||
}
|
||||
|
||||
@@ -129,8 +125,6 @@ public class CloverNoise implements NoiseGenerator {
|
||||
Vector2 p_floor = p.floor();
|
||||
|
||||
Vector2 c_11 = offset(p_floor);
|
||||
Vector2 c_10 = offset(p_floor.add(0, -1));
|
||||
Vector2 c_12 = offset(p_floor.add(0, 1));
|
||||
Vector2 c_01 = offset(p_floor.add(-1, 0));
|
||||
Vector2 c_21 = offset(p_floor.add(1, 0));
|
||||
|
||||
@@ -143,6 +137,7 @@ public class CloverNoise implements NoiseGenerator {
|
||||
Vector2 a, c, d;
|
||||
|
||||
if (side_nx < 0 && p.x < c_11.x || side_px > 0 && p.x > c_11.x) {
|
||||
Vector2 c_12 = offset(p_floor.add(0, 1));
|
||||
double side_py = m_p_c11.sub(d_p_c11.mult(c_12)).ymx();
|
||||
|
||||
if (side_py > 0) {
|
||||
@@ -155,6 +150,7 @@ public class CloverNoise implements NoiseGenerator {
|
||||
d = new Vector2(1, 1);
|
||||
}
|
||||
} else {
|
||||
Vector2 c_10 = offset(p_floor.add(0, -1));
|
||||
double side_ny = m_p_c11.sub(d_p_c11.mult(c_10)).ymx();
|
||||
|
||||
if (side_ny > 0) {
|
||||
@@ -235,6 +231,7 @@ public class CloverNoise implements NoiseGenerator {
|
||||
* @return The value of noise, from 0 to 1.
|
||||
*/
|
||||
public double fractalNoise(Vector2 p, int iterations) {
|
||||
iterations = Math.max(1, Math.min(16, iterations));
|
||||
double total = 0;
|
||||
double weight = 1;
|
||||
double div = 0;
|
||||
@@ -382,6 +379,18 @@ public class CloverNoise implements NoiseGenerator {
|
||||
private static final long HASH_M = 0x1000000000000L;
|
||||
private static final double POINT_SPREAD = 0.2;
|
||||
private static final double CURL_DX = 0.0001;
|
||||
private static final double[] HEIGHT_SINES = new double[1000];
|
||||
private static final double[] HEIGHT_COSINES = new double[1000];
|
||||
private static final ThreadLocal<Vector3[]> SAMPLE_VERTICES = ThreadLocal.withInitial(Noise3D::createSampleVertices);
|
||||
|
||||
static {
|
||||
for (int index = 0; index < HEIGHT_SINES.length; index++) {
|
||||
double height = (((index + 0.5D) / 100D) % 1D - 0.5D) * Math.PI / 2D;
|
||||
HEIGHT_SINES[index] = Math.sin(height);
|
||||
HEIGHT_COSINES[index] = Math.cos(height);
|
||||
}
|
||||
}
|
||||
|
||||
private final long seed;
|
||||
|
||||
public Noise3D(long seed) {
|
||||
@@ -392,6 +401,14 @@ public class CloverNoise implements NoiseGenerator {
|
||||
this(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private static Vector3[] createSampleVertices() {
|
||||
Vector3[] vertices = new Vector3[21];
|
||||
for (int index = 0; index < vertices.length; index++) {
|
||||
vertices[index] = new Vector3();
|
||||
}
|
||||
return vertices;
|
||||
}
|
||||
|
||||
private long doHash(long input, long seed) {
|
||||
input += seed;
|
||||
|
||||
@@ -433,27 +450,23 @@ public class CloverNoise implements NoiseGenerator {
|
||||
return (double) hash / HASH_M;
|
||||
}
|
||||
|
||||
private Vector3 offset(Vector3 position) {
|
||||
return offset(position.x, position.y, position.z);
|
||||
private Vector3 offset(Vector3 destination, Vector3 position, double xOffset, double yOffset, double zOffset) {
|
||||
return offset(destination, position.x + xOffset, position.y + yOffset, position.z + zOffset);
|
||||
}
|
||||
|
||||
private Vector3 offset(Vector3 position, double xOffset, double yOffset, double zOffset) {
|
||||
return offset(position.x + xOffset, position.y + yOffset, position.z + zOffset);
|
||||
}
|
||||
|
||||
private Vector3 offset(double positionX, double positionY, double positionZ) {
|
||||
private Vector3 offset(Vector3 destination, double positionX, double positionY, double positionZ) {
|
||||
double hash = hash(positionX, positionY, positionZ);
|
||||
double theta = hash * Math.PI * 2000;
|
||||
double height = (((Math.floor(hash * 1000) + 0.5) / 100) % 1 - 0.5) * Math.PI / 2;
|
||||
int heightIndex = (int) (hash * 1000D);
|
||||
double layer = Math.floor(hash * 10 + 1) / 10;
|
||||
double cosHeight = Math.cos(height);
|
||||
double cosHeight = HEIGHT_COSINES[heightIndex];
|
||||
double offsetX = Math.sin(theta) * cosHeight * layer + 0.5;
|
||||
double offsetY = Math.sin(height) * layer + 0.5;
|
||||
double offsetY = HEIGHT_SINES[heightIndex] * layer + 0.5;
|
||||
double offsetZ = Math.cos(theta) * cosHeight * layer + 0.5;
|
||||
return new Vector3(
|
||||
positionX + (offsetX * (POINT_SPREAD * 2) + (0.5 - POINT_SPREAD)),
|
||||
positionY + (offsetY * (POINT_SPREAD * 2) + (0.5 - POINT_SPREAD)),
|
||||
positionZ + (offsetZ * (POINT_SPREAD * 2) + (0.5 - POINT_SPREAD)));
|
||||
destination.x = positionX + (offsetX * (POINT_SPREAD * 2) + (0.5 - POINT_SPREAD));
|
||||
destination.y = positionY + (offsetY * (POINT_SPREAD * 2) + (0.5 - POINT_SPREAD));
|
||||
destination.z = positionZ + (offsetZ * (POINT_SPREAD * 2) + (0.5 - POINT_SPREAD));
|
||||
return destination;
|
||||
}
|
||||
|
||||
private boolean boundary(int permutation, Vector3 p, Vector3 c_00, Vector3 c_10, Vector3 c_20, Vector3 c_01, Vector3 c_11, Vector3 c_21, Vector3 c_02, Vector3 c_12, Vector3 c_22) {
|
||||
@@ -565,27 +578,34 @@ public class CloverNoise implements NoiseGenerator {
|
||||
* @return The value of noise, from 0 to 1.
|
||||
*/
|
||||
public double noise(Vector3 p) {
|
||||
Vector3 p_floor = p.floor();
|
||||
return sample(p, SAMPLE_VERTICES.get());
|
||||
}
|
||||
|
||||
Vector3 c_111 = offset(p_floor);
|
||||
Vector3 c_100 = offset(p_floor, 0, -1, -1);
|
||||
Vector3 c_010 = offset(p_floor, -1, 0, -1);
|
||||
Vector3 c_110 = offset(p_floor, 0, 0, -1);
|
||||
Vector3 c_210 = offset(p_floor, 1, 0, -1);
|
||||
Vector3 c_120 = offset(p_floor, 0, 1, -1);
|
||||
Vector3 c_001 = offset(p_floor, -1, -1, 0);
|
||||
Vector3 c_101 = offset(p_floor, 0, -1, 0);
|
||||
Vector3 c_201 = offset(p_floor, 1, -1, 0);
|
||||
Vector3 c_011 = offset(p_floor, -1, 0, 0);
|
||||
Vector3 c_211 = offset(p_floor, 1, 0, 0);
|
||||
Vector3 c_021 = offset(p_floor, -1, 1, 0);
|
||||
Vector3 c_121 = offset(p_floor, 0, 1, 0);
|
||||
Vector3 c_221 = offset(p_floor, 1, 1, 0);
|
||||
Vector3 c_102 = offset(p_floor, 0, -1, 1);
|
||||
Vector3 c_012 = offset(p_floor, -1, 0, 1);
|
||||
Vector3 c_112 = offset(p_floor, 0, 0, 1);
|
||||
Vector3 c_212 = offset(p_floor, 1, 0, 1);
|
||||
Vector3 c_122 = offset(p_floor, 0, 1, 1);
|
||||
private double sample(Vector3 p, Vector3[] vertices) {
|
||||
Vector3 p_floor = vertices[20];
|
||||
p_floor.x = Math.floor(p.x);
|
||||
p_floor.y = Math.floor(p.y);
|
||||
p_floor.z = Math.floor(p.z);
|
||||
|
||||
Vector3 c_111 = offset(vertices[0], p_floor.x, p_floor.y, p_floor.z);
|
||||
Vector3 c_100 = offset(vertices[1], p_floor, 0, -1, -1);
|
||||
Vector3 c_010 = offset(vertices[2], p_floor, -1, 0, -1);
|
||||
Vector3 c_110 = offset(vertices[3], p_floor, 0, 0, -1);
|
||||
Vector3 c_210 = offset(vertices[4], p_floor, 1, 0, -1);
|
||||
Vector3 c_120 = offset(vertices[5], p_floor, 0, 1, -1);
|
||||
Vector3 c_001 = offset(vertices[6], p_floor, -1, -1, 0);
|
||||
Vector3 c_101 = offset(vertices[7], p_floor, 0, -1, 0);
|
||||
Vector3 c_201 = offset(vertices[8], p_floor, 1, -1, 0);
|
||||
Vector3 c_011 = offset(vertices[9], p_floor, -1, 0, 0);
|
||||
Vector3 c_211 = offset(vertices[10], p_floor, 1, 0, 0);
|
||||
Vector3 c_021 = offset(vertices[11], p_floor, -1, 1, 0);
|
||||
Vector3 c_121 = offset(vertices[12], p_floor, 0, 1, 0);
|
||||
Vector3 c_221 = offset(vertices[13], p_floor, 1, 1, 0);
|
||||
Vector3 c_102 = offset(vertices[14], p_floor, 0, -1, 1);
|
||||
Vector3 c_012 = offset(vertices[15], p_floor, -1, 0, 1);
|
||||
Vector3 c_112 = offset(vertices[16], p_floor, 0, 0, 1);
|
||||
Vector3 c_212 = offset(vertices[17], p_floor, 1, 0, 1);
|
||||
Vector3 c_122 = offset(vertices[18], p_floor, 0, 1, 1);
|
||||
|
||||
boolean x_bound = boundary(1, p, c_100, c_110, c_120, c_101, c_111, c_121, c_102, c_112, c_122);
|
||||
boolean y_bound = boundary(2, p, c_010, c_110, c_210, c_011, c_111, c_211, c_012, c_112, c_212);
|
||||
@@ -596,7 +616,7 @@ public class CloverNoise implements NoiseGenerator {
|
||||
if (x_bound) {
|
||||
if (y_bound) {
|
||||
if (z_bound) {
|
||||
a = offset(p_floor, -1, -1, -1);
|
||||
a = offset(vertices[19], p_floor, -1, -1, -1);
|
||||
b = c_001;
|
||||
c = c_010;
|
||||
d = c_011;
|
||||
@@ -606,7 +626,7 @@ public class CloverNoise implements NoiseGenerator {
|
||||
h = c_111;
|
||||
} else {
|
||||
a = c_001;
|
||||
b = offset(p_floor, -1, -1, 1);
|
||||
b = offset(vertices[19], p_floor, -1, -1, 1);
|
||||
c = c_011;
|
||||
d = c_012;
|
||||
e = c_101;
|
||||
@@ -618,7 +638,7 @@ public class CloverNoise implements NoiseGenerator {
|
||||
if (z_bound) {
|
||||
a = c_010;
|
||||
b = c_011;
|
||||
c = offset(p_floor, -1, 1, -1);
|
||||
c = offset(vertices[19], p_floor, -1, 1, -1);
|
||||
d = c_021;
|
||||
e = c_110;
|
||||
f = c_111;
|
||||
@@ -628,7 +648,7 @@ public class CloverNoise implements NoiseGenerator {
|
||||
a = c_011;
|
||||
b = c_012;
|
||||
c = c_021;
|
||||
d = offset(p_floor, -1, 1, 1);
|
||||
d = offset(vertices[19], p_floor, -1, 1, 1);
|
||||
e = c_111;
|
||||
f = c_112;
|
||||
g = c_121;
|
||||
@@ -642,7 +662,7 @@ public class CloverNoise implements NoiseGenerator {
|
||||
b = c_101;
|
||||
c = c_110;
|
||||
d = c_111;
|
||||
e = offset(p_floor, 1, -1, -1);
|
||||
e = offset(vertices[19], p_floor, 1, -1, -1);
|
||||
f = c_201;
|
||||
g = c_210;
|
||||
h = c_211;
|
||||
@@ -652,7 +672,7 @@ public class CloverNoise implements NoiseGenerator {
|
||||
c = c_111;
|
||||
d = c_112;
|
||||
e = c_201;
|
||||
f = offset(p_floor, 1, -1, 1);
|
||||
f = offset(vertices[19], p_floor, 1, -1, 1);
|
||||
g = c_211;
|
||||
h = c_212;
|
||||
}
|
||||
@@ -664,7 +684,7 @@ public class CloverNoise implements NoiseGenerator {
|
||||
d = c_121;
|
||||
e = c_210;
|
||||
f = c_211;
|
||||
g = offset(p_floor, 1, 1, -1);
|
||||
g = offset(vertices[19], p_floor, 1, 1, -1);
|
||||
h = c_221;
|
||||
} else {
|
||||
a = c_111;
|
||||
@@ -674,7 +694,7 @@ public class CloverNoise implements NoiseGenerator {
|
||||
e = c_211;
|
||||
f = c_212;
|
||||
g = c_221;
|
||||
h = offset(p_floor, 1, 1, 1);
|
||||
h = offset(vertices[19], p_floor, 1, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -783,6 +803,7 @@ public class CloverNoise implements NoiseGenerator {
|
||||
* @return The value of noise, from 0 to 1.
|
||||
*/
|
||||
public double fractalNoise(Vector3 p, int iterations) {
|
||||
iterations = Math.max(1, Math.min(16, iterations));
|
||||
double total = 0;
|
||||
double weight = 1;
|
||||
double div = 0;
|
||||
@@ -980,6 +1001,9 @@ public class CloverNoise implements NoiseGenerator {
|
||||
|
||||
public Vector2 normalize() {
|
||||
double len2 = x * x + y * y;
|
||||
if (len2 == 0D) {
|
||||
return new Vector2(0D, 0D);
|
||||
}
|
||||
double len = Math.sqrt(len2);
|
||||
|
||||
return new Vector2(x / len, y / len);
|
||||
@@ -1092,6 +1116,9 @@ public class CloverNoise implements NoiseGenerator {
|
||||
|
||||
public Vector3 normalize() {
|
||||
double len2 = x * x + y * y + z * z;
|
||||
if (len2 == 0D) {
|
||||
return new Vector3(0D, 0D, 0D);
|
||||
}
|
||||
double len = Math.sqrt(len2);
|
||||
|
||||
return new Vector3(x / len, y / len, z / len);
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
public final class CraterNoise extends PatternNoise {
|
||||
private static final double CENTER_SPREAD = 0.6D / 65535D;
|
||||
private static final double MAX_SUPPORT_SQUARED = 0.6D * 0.6D * 1.35D;
|
||||
|
||||
private final long seed;
|
||||
|
||||
public CraterNoise(long seed) {
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
long cellX = (long) Math.floor(x);
|
||||
long cellY = (long) Math.floor(y);
|
||||
long cellZ = (long) Math.floor(z);
|
||||
double bowl = 0D;
|
||||
double rim = 0D;
|
||||
|
||||
for (int offsetY = -1; offsetY <= 1; offsetY++) {
|
||||
long pointY = cellY + offsetY;
|
||||
long hashY = pointY * 0xC2B2AE3D27D4EB4FL;
|
||||
for (int offsetZ = -1; offsetZ <= 1; offsetZ++) {
|
||||
long pointZ = cellZ + offsetZ;
|
||||
long hashYZ = hashY ^ pointZ * 0x165667B19E3779F9L;
|
||||
for (int offsetX = -1; offsetX <= 1; offsetX++) {
|
||||
long pointX = cellX + offsetX;
|
||||
long hash = mix(seed ^ pointX * 0x9E3779B97F4A7C15L ^ hashYZ);
|
||||
double deltaX = x - (pointX + 0.2D + (hash & 65535L) * CENTER_SPREAD);
|
||||
double deltaY = y - (pointY + 0.2D + ((hash >>> 16) & 65535L) * CENTER_SPREAD);
|
||||
double deltaZ = z - (pointZ + 0.2D + ((hash >>> 32) & 65535L) * CENTER_SPREAD);
|
||||
double distanceSquared = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
|
||||
if (distanceSquared >= MAX_SUPPORT_SQUARED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double radius = 0.36D + (hash >>> 56) * (0.24D / 255D);
|
||||
double relativeDistance = distanceSquared / (radius * radius);
|
||||
if (relativeDistance >= 1.35D) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double depth = ((hash >>> 48) & 255L) / 255D;
|
||||
double depression = Math.max(0D, 1D - relativeDistance);
|
||||
bowl = Math.min(bowl, -(0.28D + depth * 0.17D) * depression * depression);
|
||||
double rimDistance = (relativeDistance - 1D) / 0.35D;
|
||||
double ridge = Math.max(0D, 1D - rimDistance * rimDistance);
|
||||
rim = Math.max(rim, (0.18D + (1D - depth) * 0.12D) * ridge * ridge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0.5D + bowl + rim;
|
||||
}
|
||||
|
||||
private static long mix(long value) {
|
||||
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
|
||||
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
|
||||
return value ^ (value >>> 31);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
public final class DuneNoise extends PatternNoise {
|
||||
private static final double TAU = Math.PI * 2D;
|
||||
|
||||
private final long seed;
|
||||
private final double cosine;
|
||||
private final double sine;
|
||||
private final double phase;
|
||||
|
||||
public DuneNoise(long seed) {
|
||||
this.seed = seed;
|
||||
phase = unit(mix(seed)) * TAU;
|
||||
cosine = Math.cos(phase);
|
||||
sine = Math.sin(phase);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
double wind = x * cosine + z * sine + y * 0.17D;
|
||||
double across = z * cosine - x * sine + 0.12D * Math.sin(y * 0.7D + phase);
|
||||
long cellWind = (long) Math.floor(wind);
|
||||
long cellAcross = (long) Math.floor(across);
|
||||
double dune = 0D;
|
||||
for (int offsetWind = -1; offsetWind <= 1; offsetWind++) {
|
||||
long gridWind = cellWind + offsetWind;
|
||||
for (int offsetAcross = -1; offsetAcross <= 1; offsetAcross++) {
|
||||
long gridAcross = cellAcross + offsetAcross;
|
||||
long hash = mix(seed ^ gridWind * 0x9E3779B97F4A7C15L ^ gridAcross * 0xD1B54A32D192ED03L);
|
||||
double centerWind = gridWind + 0.2D + (hash & 65535L) * (0.6D / 65535D);
|
||||
double centerAcross = gridAcross + 0.2D + ((hash >>> 16) & 65535L) * (0.6D / 65535D);
|
||||
double width = 0.32D + ((hash >>> 32) & 255L) * (0.16D / 255D);
|
||||
double transverse = (across - centerAcross) / width;
|
||||
double transverseSquared = transverse * transverse;
|
||||
if (transverseSquared >= 1D) {
|
||||
continue;
|
||||
}
|
||||
double crest = centerWind + 0.43D * transverseSquared;
|
||||
double offset = wind - crest;
|
||||
double windward = 0.52D + ((hash >>> 40) & 255L) * (0.14D / 255D);
|
||||
double leeward = 0.1D + ((hash >>> 48) & 255L) * (0.035D / 255D);
|
||||
double slope = Math.max(0D, 1D - Math.abs(offset) / (offset < 0D ? windward : leeward));
|
||||
double horns = 1D - transverseSquared * transverseSquared;
|
||||
double height = 0.62D + (hash >>> 56) * (0.38D / 255D);
|
||||
dune = Math.max(dune, height * horns * horns * slope * slope * (3D - 2D * slope));
|
||||
}
|
||||
}
|
||||
double ripples = 0.04D + 0.035D * Math.cos(wind * TAU * 2D + 0.5D * Math.sin(across * 1.7D + phase));
|
||||
return ripples + (1D - ripples) * dune;
|
||||
}
|
||||
|
||||
private static long mix(long value) {
|
||||
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
|
||||
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
|
||||
return value ^ (value >>> 31);
|
||||
}
|
||||
|
||||
private static double unit(long value) {
|
||||
return (value >>> 11) * 0x1.0p-53D;
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ public class FastNoise {
|
||||
}
|
||||
|
||||
private static int FastFloor(float f) {
|
||||
return (f >= 0 ? (int) f : (int) f - 1);
|
||||
return (int) Math.floor(f);
|
||||
}
|
||||
|
||||
private static int FastRound(float f) {
|
||||
@@ -259,7 +259,7 @@ public class FastNoise {
|
||||
// Sets octave count for all fractal noise types
|
||||
// Default: 3
|
||||
public void SetFractalOctaves(int octaves) {
|
||||
m_octaves = octaves;
|
||||
m_octaves = Math.max(1, Math.min(16, octaves));
|
||||
CalculateFractalBounding();
|
||||
}
|
||||
|
||||
@@ -1515,7 +1515,7 @@ public class FastNoise {
|
||||
x *= m_frequency;
|
||||
y *= m_frequency;
|
||||
|
||||
return SingleCubic(0, x, y);
|
||||
return SingleCubic(m_seed, x, y);
|
||||
}
|
||||
|
||||
private float SingleCubic(int seed, float x, float y) {
|
||||
@@ -2194,4 +2194,4 @@ public class FastNoise {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public class FastNoiseDouble {
|
||||
}
|
||||
|
||||
private static long fastFloor(double f) {
|
||||
return (f >= 0 ? (long) f : (long) f - 1);
|
||||
return (long) Math.floor(f);
|
||||
}
|
||||
|
||||
private static long fastRound(double f) {
|
||||
@@ -289,7 +289,7 @@ public class FastNoiseDouble {
|
||||
// Sets octave count for all fractal noise types
|
||||
// Default: 3
|
||||
public void setFractalOctaves(long octaves) {
|
||||
m_octaves = octaves;
|
||||
m_octaves = Math.max(1, Math.min(16, octaves));
|
||||
calculateFractalBounding();
|
||||
}
|
||||
|
||||
@@ -1453,7 +1453,7 @@ public class FastNoiseDouble {
|
||||
x *= m_frequency;
|
||||
y *= m_frequency;
|
||||
|
||||
return SingleCubic(0, x, y);
|
||||
return SingleCubic(m_seed, x, y);
|
||||
}
|
||||
|
||||
private double SingleCubic(long seed, double x, double y) {
|
||||
@@ -1676,14 +1676,11 @@ public class FastNoiseDouble {
|
||||
break;
|
||||
}
|
||||
|
||||
return switch (m_cellularReturnType) {
|
||||
case Distance2 -> distance2 - 1;
|
||||
case Distance2Add -> distance2 + distance - 1;
|
||||
case Distance2Sub -> distance2 - distance - 1;
|
||||
case Distance2Mul -> distance2 * distance - 1;
|
||||
case Distance2Div -> distance / distance2 - 1;
|
||||
default -> 0;
|
||||
};
|
||||
double boundary = 1.55D - Math.max(Math.abs(x - xr), Math.max(Math.abs(y - yr), Math.abs(z - zr)));
|
||||
if (cellularDistance(boundary, 0D, 0D) < distance2) {
|
||||
return refineCellular2Edge(x, y, z, xr, yr, zr, distance, distance2);
|
||||
}
|
||||
return cellular2EdgeResult(distance, distance2);
|
||||
}
|
||||
|
||||
public double GetCellular(double x, double y) {
|
||||
@@ -1856,6 +1853,93 @@ public class FastNoiseDouble {
|
||||
break;
|
||||
}
|
||||
|
||||
double boundary = 1.55D - Math.max(Math.abs(x - xr), Math.abs(y - yr));
|
||||
if (cellularDistance(boundary, 0D, 0D) < distance2) {
|
||||
return refineCellular2Edge(x, y, xr, yr, distance, distance2);
|
||||
}
|
||||
return cellular2EdgeResult(distance, distance2);
|
||||
}
|
||||
|
||||
private double refineCellular2Edge(double x, double y, long xr, long yr, double distance, double distance2) {
|
||||
for (int dx = -2; dx <= 2; dx++) {
|
||||
long xi = xr + dx;
|
||||
double baseX = xi - x;
|
||||
double boundX = Math.max(0D, Math.abs(baseX) - 0.45D);
|
||||
if (cellularDistance(boundX, 0D, 0D) >= distance2) {
|
||||
continue;
|
||||
}
|
||||
for (int dy = -2; dy <= 2; dy++) {
|
||||
if (Math.abs(dx) < 2 && Math.abs(dy) < 2) {
|
||||
continue;
|
||||
}
|
||||
long yi = yr + dy;
|
||||
double baseY = yi - y;
|
||||
double boundY = Math.max(0D, Math.abs(baseY) - 0.45D);
|
||||
if (cellularDistance(boundX, boundY, 0D) >= distance2) {
|
||||
continue;
|
||||
}
|
||||
int index = (int) hash2D(m_seed, xi, yi) & 255;
|
||||
double candidate = cellularDistance(baseX + CELL_2D_X[index], baseY + CELL_2D_Y[index], 0D);
|
||||
if (candidate < distance) {
|
||||
distance2 = distance;
|
||||
distance = candidate;
|
||||
} else if (candidate < distance2) {
|
||||
distance2 = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cellular2EdgeResult(distance, distance2);
|
||||
}
|
||||
|
||||
private double refineCellular2Edge(double x, double y, double z, long xr, long yr, long zr, double distance, double distance2) {
|
||||
int radius = m_cellularDistanceFunction == CellularDistanceFunction.Manhattan ? 3 : 2;
|
||||
for (int dx = -radius; dx <= radius; dx++) {
|
||||
long xi = xr + dx;
|
||||
double baseX = xi - x;
|
||||
double boundX = Math.max(0D, Math.abs(baseX) - 0.45D);
|
||||
if (cellularDistance(boundX, 0D, 0D) >= distance2) {
|
||||
continue;
|
||||
}
|
||||
for (int dy = -radius; dy <= radius; dy++) {
|
||||
long yi = yr + dy;
|
||||
double baseY = yi - y;
|
||||
double boundY = Math.max(0D, Math.abs(baseY) - 0.45D);
|
||||
if (cellularDistance(boundX, boundY, 0D) >= distance2) {
|
||||
continue;
|
||||
}
|
||||
for (int dz = -radius; dz <= radius; dz++) {
|
||||
if (Math.abs(dx) < 2 && Math.abs(dy) < 2 && Math.abs(dz) < 2) {
|
||||
continue;
|
||||
}
|
||||
long zi = zr + dz;
|
||||
double baseZ = zi - z;
|
||||
double boundZ = Math.max(0D, Math.abs(baseZ) - 0.45D);
|
||||
if (cellularDistance(boundX, boundY, boundZ) >= distance2) {
|
||||
continue;
|
||||
}
|
||||
int index = (int) hash3D(m_seed, xi, yi, zi) & 255;
|
||||
double candidate = cellularDistance(baseX + CELL_3D_X[index], baseY + CELL_3D_Y[index], baseZ + CELL_3D_Z[index]);
|
||||
if (candidate < distance) {
|
||||
distance2 = distance;
|
||||
distance = candidate;
|
||||
} else if (candidate < distance2) {
|
||||
distance2 = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cellular2EdgeResult(distance, distance2);
|
||||
}
|
||||
|
||||
private double cellularDistance(double x, double y, double z) {
|
||||
return switch (m_cellularDistanceFunction) {
|
||||
case Euclidean -> x * x + y * y + z * z;
|
||||
case Manhattan -> Math.abs(x) + Math.abs(y) + Math.abs(z);
|
||||
case Natural -> (Math.abs(x) + Math.abs(y) + Math.abs(z)) + (x * x + y * y + z * z);
|
||||
};
|
||||
}
|
||||
|
||||
private double cellular2EdgeResult(double distance, double distance2) {
|
||||
return switch (m_cellularReturnType) {
|
||||
case Distance2 -> distance2 - 1;
|
||||
case Distance2Add -> distance2 + distance - 1;
|
||||
|
||||
@@ -25,6 +25,7 @@ public class FractalBillowPerlinNoise implements NoiseGenerator, OctaveNoise {
|
||||
|
||||
public FractalBillowPerlinNoise(long seed) {
|
||||
this.n = new FastNoiseDouble(new RNG(seed).lmax());
|
||||
n.setLongerp(FastNoiseDouble.Longerp.Qulongic);
|
||||
n.setFractalOctaves(1);
|
||||
n.setFractalType(FractalType.Billow);
|
||||
}
|
||||
|
||||
@@ -20,11 +20,12 @@ package art.arcane.iris.util.project.noise;
|
||||
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import art.arcane.iris.util.project.noise.FastNoiseDouble.FractalType;
|
||||
public class FractalCubicNoise implements NoiseGenerator {
|
||||
public class FractalCubicNoise implements NoiseGenerator, OctaveNoise {
|
||||
private final FastNoiseDouble n;
|
||||
|
||||
public FractalCubicNoise(long seed) {
|
||||
this.n = new FastNoiseDouble(new RNG(seed).lmax());
|
||||
n.setFractalOctaves(1);
|
||||
n.setFractalType(FractalType.Billow);
|
||||
}
|
||||
|
||||
@@ -46,4 +47,9 @@ public class FractalCubicNoise implements NoiseGenerator {
|
||||
public double noise(double x, double y, double z) {
|
||||
return f(n.GetCubicFractal(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOctaves(int octaves) {
|
||||
n.setFractalOctaves(octaves);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ public class FractalRigidMultiSimplexNoise implements NoiseGenerator, OctaveNois
|
||||
}
|
||||
|
||||
public double f(double v) {
|
||||
return (v / 2D) + 0.5D;
|
||||
return 1D + (v - 1D) * n.getFractalBounding();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import java.util.SplittableRandom;
|
||||
|
||||
public final class GaborNoise extends PatternNoise {
|
||||
private static final double TAU = Math.PI * 2D;
|
||||
private static final double RADIUS_SQUARED = 0.95D * 0.95D;
|
||||
private static final double SPREAD = 0.6D / 65535D;
|
||||
|
||||
private final long seed;
|
||||
private final double directionX;
|
||||
private final double directionY;
|
||||
private final double directionZ;
|
||||
|
||||
public GaborNoise(long seed) {
|
||||
this.seed = seed;
|
||||
SplittableRandom random = new SplittableRandom(seed);
|
||||
double angle = random.nextDouble(TAU);
|
||||
double elevation = random.nextDouble(-0.55D, 0.55D);
|
||||
double horizontal = Math.sqrt(1D - elevation * elevation);
|
||||
directionX = Math.cos(angle) * horizontal;
|
||||
directionY = elevation;
|
||||
directionZ = Math.sin(angle) * horizontal;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
long cellX = (long) Math.floor(x);
|
||||
long cellY = (long) Math.floor(y);
|
||||
long cellZ = (long) Math.floor(z);
|
||||
double sum = 0D;
|
||||
double weights = 0.12D;
|
||||
for (int offsetY = -1; offsetY <= 1; offsetY++) {
|
||||
long pointY = cellY + offsetY;
|
||||
long hashY = pointY * 0xC2B2AE3D27D4EB4FL;
|
||||
for (int offsetZ = -1; offsetZ <= 1; offsetZ++) {
|
||||
long pointZ = cellZ + offsetZ;
|
||||
long hashYZ = hashY ^ pointZ * 0x165667B19E3779F9L;
|
||||
for (int offsetX = -1; offsetX <= 1; offsetX++) {
|
||||
long pointX = cellX + offsetX;
|
||||
long hash = mix(seed ^ pointX * 0x9E3779B97F4A7C15L ^ hashYZ);
|
||||
if ((hash & 3L) == 0L) {
|
||||
continue;
|
||||
}
|
||||
double dx = x - (pointX + 0.2D + (hash & 65535L) * SPREAD);
|
||||
double dy = y - (pointY + 0.2D + ((hash >>> 16) & 65535L) * SPREAD);
|
||||
double dz = z - (pointZ + 0.2D + ((hash >>> 32) & 65535L) * SPREAD);
|
||||
double distanceSquared = dx * dx + dy * dy + dz * dz;
|
||||
if (distanceSquared >= RADIUS_SQUARED) {
|
||||
continue;
|
||||
}
|
||||
double envelope = 1D - distanceSquared / RADIUS_SQUARED;
|
||||
double weight = envelope * envelope * envelope;
|
||||
long wave = mix(hash);
|
||||
double waveX = directionX + ((wave & 1023L) / 1023D - 0.5D) * 0.35D;
|
||||
double waveY = directionY + (((wave >>> 10) & 1023L) / 1023D - 0.5D) * 0.35D;
|
||||
double waveZ = directionZ + (((wave >>> 20) & 1023L) / 1023D - 0.5D) * 0.35D;
|
||||
double phase = (wave >>> 32) * (TAU / 4294967296D);
|
||||
sum += weight * Math.cos(TAU * 3D * (waveX * dx + waveY * dy + waveZ * dz) + phase);
|
||||
weights += weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0.5D + 0.5D * sum / weights;
|
||||
}
|
||||
|
||||
private static long mix(long value) {
|
||||
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
|
||||
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
|
||||
return value ^ (value >>> 31);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import java.util.SplittableRandom;
|
||||
|
||||
public final class GyroidNoise extends PatternNoise {
|
||||
private static final double TAU = Math.PI * 2D;
|
||||
|
||||
private final double horizontalCosine;
|
||||
private final double horizontalSine;
|
||||
private final double tiltCosine;
|
||||
private final double tiltSine;
|
||||
private final double phaseX;
|
||||
private final double phaseY;
|
||||
private final double phaseZ;
|
||||
|
||||
public GyroidNoise(long seed) {
|
||||
SplittableRandom random = new SplittableRandom(seed);
|
||||
double horizontalAngle = random.nextDouble(TAU);
|
||||
double tiltAngle = random.nextDouble(0.35D, 1.2D);
|
||||
horizontalCosine = Math.cos(horizontalAngle);
|
||||
horizontalSine = Math.sin(horizontalAngle);
|
||||
tiltCosine = Math.cos(tiltAngle);
|
||||
tiltSine = Math.sin(tiltAngle);
|
||||
phaseX = random.nextDouble(TAU);
|
||||
phaseY = random.nextDouble(TAU);
|
||||
phaseZ = random.nextDouble(TAU);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
double horizontal = x * horizontalCosine + z * horizontalSine;
|
||||
double transverse = z * horizontalCosine - x * horizontalSine;
|
||||
double u = TAU * (horizontal * tiltCosine + y * tiltSine) + phaseX;
|
||||
double v = TAU * (y * tiltCosine - horizontal * tiltSine) + phaseY;
|
||||
double w = TAU * transverse + phaseZ;
|
||||
double a = u + 0.7D * Math.sin(0.37D * v + 0.29D * w + phaseZ);
|
||||
double b = v + 0.7D * Math.sin(0.31D * w - 0.23D * u + phaseX);
|
||||
double c = w + 0.7D * Math.sin(0.41D * u + 0.19D * v + phaseY);
|
||||
double field = Math.sin(a) * Math.cos(b) + Math.sin(b) * Math.cos(c) + Math.sin(c) * Math.cos(a);
|
||||
double width = 0.62D + 0.22D * Math.sin(0.29D * u + 0.23D * v - 0.19D * w);
|
||||
double sheet = Math.max(0D, 1D - Math.abs(field) / width);
|
||||
return sheet * sheet * (3D - 2D * sheet);
|
||||
}
|
||||
}
|
||||
@@ -2,294 +2,89 @@ package art.arcane.iris.util.project.noise;
|
||||
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
|
||||
public class HexJamesNoise implements NoiseGenerator, OctaveNoise {
|
||||
private static final double SQRT_3_OVER_3 = Math.sqrt(3.0) / 3.0;
|
||||
private static final double TWO_OVER_THREE = 2.0 / 3.0;
|
||||
private static final double SQRT_3 = Math.sqrt(3.0);
|
||||
private static final double THREE_OVER_TWO = 1.5D;
|
||||
private static final double ONE_THIRD = 1D / 3D;
|
||||
private static final double TWO_THIRDS = 2D / 3D;
|
||||
public class HexJamesNoise extends HexagonalNoise implements OctaveNoise {
|
||||
private static final int MAX_DEPTH = 7;
|
||||
private static final double HEAT_SCALE = 0.31D;
|
||||
private static final double HEAT_SCALE = 18D;
|
||||
private static final double LARGE_CONTINUE = 0.26D;
|
||||
private static final double SMALL_CONTINUE = 0.79D;
|
||||
private static final double CENTER_CONTINUE = 0.58D;
|
||||
private static final long CONST_X = 0x9E3779B97F4A7C15L;
|
||||
private static final long CONST_Z = 0xC2B2AE3D27D4EB4FL;
|
||||
private static final long[][] CHILD_DIRECTIONS = new long[][]{
|
||||
{1L, 0L},
|
||||
{1L, -1L},
|
||||
{0L, -1L},
|
||||
{-1L, 0L},
|
||||
{-1L, 1L},
|
||||
{0L, 1L}
|
||||
};
|
||||
private static final ThreadLocal<HexScratch> SCRATCH = ThreadLocal.withInitial(HexScratch::new);
|
||||
private final long seed;
|
||||
private final SimplexNoise heatSimplex;
|
||||
private int octaves;
|
||||
private final SimplexNoise heat;
|
||||
|
||||
public HexJamesNoise(long seed) {
|
||||
RNG rng = new RNG(seed);
|
||||
this.seed = rng.lmax();
|
||||
this.heatSimplex = new SimplexNoise(rng.nextParallelRNG(877L).lmax());
|
||||
this.octaves = 1;
|
||||
this.heat = new SimplexNoise(rng.nextParallelRNG(877L).lmax());
|
||||
}
|
||||
|
||||
private double clip(double value) {
|
||||
if (value < 0D) {
|
||||
return 0D;
|
||||
}
|
||||
|
||||
if (value > 1D) {
|
||||
return 1D;
|
||||
}
|
||||
|
||||
return value;
|
||||
@Override
|
||||
public void setOctaves(int octaves) {
|
||||
heat.setOctaves(octaves);
|
||||
}
|
||||
|
||||
private long mix(long input) {
|
||||
input = (input ^ (input >>> 33)) * 0xff51afd7ed558ccdL;
|
||||
input = (input ^ (input >>> 33)) * 0xc4ceb9fe1a85ec53L;
|
||||
return input ^ (input >>> 33);
|
||||
}
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
double qWorld = (SQRT_3_OVER_3 * x) - (z / 3D);
|
||||
double rWorld = TWO_OVER_THREE * z;
|
||||
long rootQ = roundQ(qWorld, rWorld);
|
||||
long rootR = roundR(qWorld, rWorld);
|
||||
double centerQ = rootQ;
|
||||
double centerR = rootR;
|
||||
double radius = 1D;
|
||||
double localQ = qWorld - centerQ;
|
||||
double localR = rWorld - centerR;
|
||||
long nodeHash = mix(seed ^ (rootQ * CONST_X) ^ (rootR * CONST_Z));
|
||||
int level = 0;
|
||||
|
||||
private double hashToUnit(long value) {
|
||||
long normalized = mix(value) & Long.MAX_VALUE;
|
||||
return normalized / (double) Long.MAX_VALUE;
|
||||
}
|
||||
while (level < MAX_DEPTH) {
|
||||
int childIndex = pickChildIndex(localQ, localR);
|
||||
if (childIndex < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
private double random01(long nodeHash, int salt) {
|
||||
long mixed = nodeHash ^ (CONST_X * (salt + 1L)) ^ (CONST_Z * (salt + 7L));
|
||||
return hashToUnit(mixed);
|
||||
}
|
||||
double childQ = CHILD_Q[childIndex];
|
||||
double childR = CHILD_R[childIndex];
|
||||
centerQ += childQ * radius;
|
||||
centerR += childR * radius;
|
||||
radius *= ONE_THIRD;
|
||||
localQ = (localQ - childQ) * 3D;
|
||||
localR = (localR - childR) * 3D;
|
||||
nodeHash = mix(nodeHash ^ (CONST_X * (childIndex + 11L)) ^ (CONST_Z * (level + 1L)));
|
||||
level++;
|
||||
|
||||
private long nextNodeHash(long nodeHash, int childIndex, int level) {
|
||||
long mixed = nodeHash ^ (CONST_X * (childIndex + 11L)) ^ (CONST_Z * (level + 1L));
|
||||
return mix(mixed);
|
||||
}
|
||||
|
||||
private long[] roundAxial(double q, double r) {
|
||||
double cubeX = q;
|
||||
double cubeZ = r;
|
||||
double cubeY = -cubeX - cubeZ;
|
||||
long roundedX = Math.round(cubeX);
|
||||
long roundedY = Math.round(cubeY);
|
||||
long roundedZ = Math.round(cubeZ);
|
||||
double xDiff = Math.abs(roundedX - cubeX);
|
||||
double yDiff = Math.abs(roundedY - cubeY);
|
||||
double zDiff = Math.abs(roundedZ - cubeZ);
|
||||
|
||||
if (xDiff > yDiff && xDiff > zDiff) {
|
||||
roundedX = -roundedY - roundedZ;
|
||||
} else if (yDiff > zDiff) {
|
||||
roundedY = -roundedX - roundedZ;
|
||||
} else {
|
||||
roundedZ = -roundedX - roundedY;
|
||||
}
|
||||
|
||||
return new long[]{roundedX, roundedZ};
|
||||
}
|
||||
|
||||
private double hexDistance(double q, double r, double centerQ, double centerR) {
|
||||
double deltaQ = q - centerQ;
|
||||
double deltaR = r - centerR;
|
||||
double deltaY = -deltaQ - deltaR;
|
||||
return Math.max(Math.abs(deltaQ), Math.max(Math.abs(deltaR), Math.abs(deltaY)));
|
||||
}
|
||||
|
||||
private double axialX(double q, double r) {
|
||||
return SQRT_3 * (q + (r * 0.5D));
|
||||
}
|
||||
|
||||
private double axialZ(double r) {
|
||||
return THREE_OVER_TWO * r;
|
||||
}
|
||||
|
||||
private double simplexField(double x, double z, double scale) {
|
||||
if (octaves <= 1) {
|
||||
return heatSimplex.noise(x * scale, z * scale);
|
||||
}
|
||||
|
||||
double frequency = 1D;
|
||||
double amplitude = 1D;
|
||||
double total = 0D;
|
||||
double max = 0D;
|
||||
|
||||
for (int i = 0; i < octaves; i++) {
|
||||
total += heatSimplex.noise((x * scale) * frequency, (z * scale) * frequency) * amplitude;
|
||||
max += amplitude;
|
||||
frequency *= 2D;
|
||||
amplitude *= 0.5D;
|
||||
}
|
||||
|
||||
if (max == 0D) {
|
||||
return 0.5D;
|
||||
}
|
||||
|
||||
return clip(total / max);
|
||||
}
|
||||
|
||||
private double cellHeat(double centerQ, double centerR, long nodeHash, int level) {
|
||||
double x = axialX(centerQ, centerR);
|
||||
double z = axialZ(centerR);
|
||||
double scale = HEAT_SCALE * (1D + (level * 0.24D));
|
||||
double simplexValue = simplexField(x, z, scale);
|
||||
double randomValue = random01(nodeHash, 31 + level);
|
||||
return clip((simplexValue * 0.82D) + (randomValue * 0.18D));
|
||||
}
|
||||
|
||||
private int rotationForNode(long nodeHash) {
|
||||
int rotation = (int) Math.floor(random01(nodeHash, 3) * 6D);
|
||||
if (rotation < 0) {
|
||||
rotation = 0;
|
||||
}
|
||||
|
||||
if (rotation > 5) {
|
||||
rotation = 5;
|
||||
}
|
||||
|
||||
return rotation;
|
||||
}
|
||||
|
||||
private int parityForNode(long nodeHash) {
|
||||
return random01(nodeHash, 5) >= 0.5D ? 1 : 0;
|
||||
}
|
||||
|
||||
private int ringIndexForDirection(int direction, int rotation) {
|
||||
int ring = direction - rotation;
|
||||
|
||||
while (ring < 0) {
|
||||
ring += 6;
|
||||
}
|
||||
|
||||
while (ring >= 6) {
|
||||
ring -= 6;
|
||||
}
|
||||
|
||||
return ring;
|
||||
}
|
||||
|
||||
private int pickChildIndex(double localQ, double localR, double[] centersQ, double[] centersR) {
|
||||
int bestIndex = -1;
|
||||
double bestDistance = Double.POSITIVE_INFINITY;
|
||||
|
||||
centersQ[0] = 0D;
|
||||
centersR[0] = 0D;
|
||||
double centerDistance = hexDistance(localQ, localR, 0D, 0D) / ONE_THIRD;
|
||||
|
||||
if (centerDistance <= 1D && centerDistance < bestDistance) {
|
||||
bestDistance = centerDistance;
|
||||
bestIndex = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
int index = i + 1;
|
||||
double centerQ = CHILD_DIRECTIONS[i][0] * TWO_THIRDS;
|
||||
double centerR = CHILD_DIRECTIONS[i][1] * TWO_THIRDS;
|
||||
centersQ[index] = centerQ;
|
||||
centersR[index] = centerR;
|
||||
double distance = hexDistance(localQ, localR, centerQ, centerR) / ONE_THIRD;
|
||||
|
||||
if (distance <= 1D && distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
bestIndex = index;
|
||||
if (!shouldContinue(childIndex, nodeHash, level - 1)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return bestIndex;
|
||||
double centerX = SQRT_3 * (centerQ + (centerR * 0.5D));
|
||||
double centerZ = 1.5D * centerR;
|
||||
double simplexValue = heat.noise(centerX * HEAT_SCALE, y * HEAT_SCALE, centerZ * HEAT_SCALE);
|
||||
return (simplexValue * 0.82D) + (random01(nodeHash, 31 + level) * 0.18D);
|
||||
}
|
||||
|
||||
private boolean shouldContinue(int level, int childIndex, long nodeHash) {
|
||||
private boolean shouldContinue(int childIndex, long nodeHash, int level) {
|
||||
if (level >= MAX_DEPTH - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double gate;
|
||||
|
||||
if (childIndex == 0) {
|
||||
gate = CENTER_CONTINUE;
|
||||
} else {
|
||||
int rotation = rotationForNode(nodeHash);
|
||||
int parity = parityForNode(nodeHash);
|
||||
int ringIndex = ringIndexForDirection(childIndex - 1, rotation);
|
||||
boolean large = (ringIndex % 2) == parity;
|
||||
gate = large ? LARGE_CONTINUE : SMALL_CONTINUE;
|
||||
}
|
||||
|
||||
double randomValue = random01(nodeHash, 97 + level);
|
||||
return randomValue <= gate;
|
||||
}
|
||||
|
||||
private double sample(double x, double z) {
|
||||
HexScratch scratch = SCRATCH.get();
|
||||
double[] centersQ = scratch.centersQ;
|
||||
double[] centersR = scratch.centersR;
|
||||
double qWorld = (SQRT_3_OVER_3 * x) - (z / 3.0);
|
||||
double rWorld = TWO_OVER_THREE * z;
|
||||
long[] rounded = roundAxial(qWorld, rWorld);
|
||||
double centerQ = rounded[0];
|
||||
double centerR = rounded[1];
|
||||
double radius = 0.5D;
|
||||
double localQ = (qWorld - centerQ) / radius;
|
||||
double localR = (rWorld - centerR) / radius;
|
||||
long nodeHash = mix(seed ^ (rounded[0] * CONST_X) ^ (rounded[1] * CONST_Z));
|
||||
double heat = cellHeat(centerQ, centerR, nodeHash, 0);
|
||||
|
||||
for (int level = 0; level < MAX_DEPTH; level++) {
|
||||
int childIndex = pickChildIndex(localQ, localR, centersQ, centersR);
|
||||
|
||||
if (childIndex < 0) {
|
||||
return heat;
|
||||
}
|
||||
|
||||
double childCenterQ = centersQ[childIndex];
|
||||
double childCenterR = centersR[childIndex];
|
||||
centerQ += childCenterQ * radius;
|
||||
centerR += childCenterR * radius;
|
||||
radius *= ONE_THIRD;
|
||||
localQ = (localQ - childCenterQ) / ONE_THIRD;
|
||||
localR = (localR - childCenterR) / ONE_THIRD;
|
||||
nodeHash = nextNodeHash(nodeHash, childIndex, level);
|
||||
heat = cellHeat(centerQ, centerR, nodeHash, level + 1);
|
||||
|
||||
if (!shouldContinue(level, childIndex, nodeHash)) {
|
||||
return heat;
|
||||
double gate = CENTER_CONTINUE;
|
||||
if (childIndex != 0) {
|
||||
int rotation = (int) (random01(nodeHash, 3) * 6D);
|
||||
int parity = random01(nodeHash, 5) >= 0.5D ? 1 : 0;
|
||||
int direction = childIndex - 1 - rotation;
|
||||
if (direction < 0) {
|
||||
direction += 6;
|
||||
}
|
||||
gate = (direction % 2) == parity ? LARGE_CONTINUE : SMALL_CONTINUE;
|
||||
}
|
||||
|
||||
return heat;
|
||||
return random01(nodeHash, 97 + level) <= gate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x) {
|
||||
return sample(x, 0D);
|
||||
private double random01(long nodeHash, int salt) {
|
||||
long mixed = nodeHash ^ (CONST_X * (salt + 1L)) ^ (CONST_Z * (salt + 7L));
|
||||
return (mix(mixed) >>> 11) * 0x1.0p-53;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double z) {
|
||||
return sample(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double y, double z) {
|
||||
if (z == 0D) {
|
||||
return sample(x, y);
|
||||
}
|
||||
|
||||
double a = sample(x, z);
|
||||
double b = sample(y, x);
|
||||
double c = sample(z, y);
|
||||
return (a + b + c) / 3D;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOctaves(int o) {
|
||||
this.octaves = Math.max(1, o);
|
||||
}
|
||||
|
||||
private static final class HexScratch {
|
||||
private final double[] centersQ = new double[7];
|
||||
private final double[] centersR = new double[7];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,259 +2,77 @@ package art.arcane.iris.util.project.noise;
|
||||
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
|
||||
public class HexRandomSizeNoise implements NoiseGenerator, OctaveNoise {
|
||||
private static final double SQRT_3_OVER_3 = Math.sqrt(3.0) / 3.0;
|
||||
private static final double TWO_OVER_THREE = 2.0 / 3.0;
|
||||
private static final double SQRT_3 = Math.sqrt(3.0);
|
||||
private static final double THREE_OVER_TWO = 1.5D;
|
||||
private static final double ONE_THIRD = 1D / 3D;
|
||||
private static final double TWO_THIRDS = 2D / 3D;
|
||||
public class HexRandomSizeNoise extends HexagonalNoise implements OctaveNoise {
|
||||
private static final int MAX_DEPTH = 8;
|
||||
private static final double HEAT_SCALE = 0.24D;
|
||||
private static final double STRUCTURE_SCALE = 0.36D;
|
||||
private static final double HEAT_SCALE = 18D;
|
||||
private static final double STRUCTURE_SCALE = 36D;
|
||||
private static final double SUBDIVIDE_BASE_THRESHOLD = 0.22D;
|
||||
private static final double SUBDIVIDE_LEVEL_STEP = 0.11D;
|
||||
private static final long CONST_X = 0x9E3779B97F4A7C15L;
|
||||
private static final long CONST_Z = 0xC2B2AE3D27D4EB4FL;
|
||||
private static final long[][] CHILD_DIRECTIONS = new long[][]{
|
||||
{1L, 0L},
|
||||
{1L, -1L},
|
||||
{0L, -1L},
|
||||
{-1L, 0L},
|
||||
{-1L, 1L},
|
||||
{0L, 1L}
|
||||
};
|
||||
private static final ThreadLocal<HexScratch> SCRATCH = ThreadLocal.withInitial(HexScratch::new);
|
||||
private final long seed;
|
||||
private final SimplexNoise heatSimplex;
|
||||
private final SimplexNoise structureSimplex;
|
||||
private int octaves;
|
||||
private final SimplexNoise heat;
|
||||
private final SimplexNoise structure;
|
||||
|
||||
public HexRandomSizeNoise(long seed) {
|
||||
RNG rng = new RNG(seed);
|
||||
this.seed = rng.lmax();
|
||||
this.heatSimplex = new SimplexNoise(rng.nextParallelRNG(221L).lmax());
|
||||
this.structureSimplex = new SimplexNoise(rng.nextParallelRNG(442L).lmax());
|
||||
this.octaves = 1;
|
||||
this.heat = new SimplexNoise(rng.nextParallelRNG(221L).lmax());
|
||||
this.structure = new SimplexNoise(rng.nextParallelRNG(442L).lmax());
|
||||
}
|
||||
|
||||
private double clip(double value) {
|
||||
if (value < 0D) {
|
||||
return 0D;
|
||||
@Override
|
||||
public void setOctaves(int octaves) {
|
||||
heat.setOctaves(octaves);
|
||||
structure.setOctaves(octaves);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
double qWorld = (SQRT_3_OVER_3 * x) - (z / 3D);
|
||||
double rWorld = TWO_OVER_THREE * z;
|
||||
long rootQ = roundQ(qWorld, rWorld);
|
||||
long rootR = roundR(qWorld, rWorld);
|
||||
double centerQ = rootQ;
|
||||
double centerR = rootR;
|
||||
double radius = 1D;
|
||||
double localQ = qWorld - centerQ;
|
||||
double localR = rWorld - centerR;
|
||||
long nodeHash = mix(seed ^ (rootQ * CONST_X) ^ (rootR * CONST_Z));
|
||||
int level = 0;
|
||||
|
||||
while (level < MAX_DEPTH - 1 && shouldSubdivide(centerQ, centerR, nodeHash, level)) {
|
||||
int childIndex = pickChildIndex(localQ, localR);
|
||||
if (childIndex < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
double childQ = CHILD_Q[childIndex];
|
||||
double childR = CHILD_R[childIndex];
|
||||
centerQ += childQ * radius;
|
||||
centerR += childR * radius;
|
||||
radius *= ONE_THIRD;
|
||||
localQ = (localQ - childQ) * 3D;
|
||||
localR = (localR - childR) * 3D;
|
||||
nodeHash = mix(nodeHash ^ (CONST_Z * (childIndex + 1L)) ^ (CONST_X * (level + 1L)));
|
||||
level++;
|
||||
}
|
||||
|
||||
if (value > 1D) {
|
||||
return 1D;
|
||||
}
|
||||
|
||||
return value;
|
||||
double centerX = SQRT_3 * (centerQ + (centerR * 0.5D));
|
||||
double centerZ = 1.5D * centerR;
|
||||
double simplexValue = heat.noise(centerX * HEAT_SCALE, y * HEAT_SCALE, centerZ * HEAT_SCALE);
|
||||
return (simplexValue * 0.74D) + (random01(nodeHash, 11 + level) * 0.26D);
|
||||
}
|
||||
|
||||
private long mix(long input) {
|
||||
input = (input ^ (input >>> 33)) * 0xff51afd7ed558ccdL;
|
||||
input = (input ^ (input >>> 33)) * 0xc4ceb9fe1a85ec53L;
|
||||
return input ^ (input >>> 33);
|
||||
}
|
||||
|
||||
private double hashToUnit(long value) {
|
||||
long normalized = mix(value) & Long.MAX_VALUE;
|
||||
return normalized / (double) Long.MAX_VALUE;
|
||||
private boolean shouldSubdivide(double centerQ, double centerR, long nodeHash, int level) {
|
||||
double centerX = SQRT_3 * (centerQ + (centerR * 0.5D));
|
||||
double centerZ = 1.5D * centerR;
|
||||
double simplexValue = structure.noise(centerX * STRUCTURE_SCALE, centerZ * STRUCTURE_SCALE);
|
||||
double score = (simplexValue * 0.68D) + (random01(nodeHash, 71 + level) * 0.32D);
|
||||
return score > SUBDIVIDE_BASE_THRESHOLD + (level * SUBDIVIDE_LEVEL_STEP);
|
||||
}
|
||||
|
||||
private double random01(long nodeHash, int salt) {
|
||||
long mixed = nodeHash ^ (CONST_X * (salt + 1L)) ^ (CONST_Z * (salt + 31L));
|
||||
return hashToUnit(mixed);
|
||||
return (mix(mixed) >>> 11) * 0x1.0p-53;
|
||||
}
|
||||
|
||||
private long nextNodeHash(long nodeHash, int childIndex, int level) {
|
||||
long mixed = nodeHash ^ (CONST_Z * (childIndex + 1L)) ^ (CONST_X * (level + 1L));
|
||||
return mix(mixed);
|
||||
}
|
||||
|
||||
private long[] roundAxial(double q, double r) {
|
||||
double cubeX = q;
|
||||
double cubeZ = r;
|
||||
double cubeY = -cubeX - cubeZ;
|
||||
long roundedX = Math.round(cubeX);
|
||||
long roundedY = Math.round(cubeY);
|
||||
long roundedZ = Math.round(cubeZ);
|
||||
double xDiff = Math.abs(roundedX - cubeX);
|
||||
double yDiff = Math.abs(roundedY - cubeY);
|
||||
double zDiff = Math.abs(roundedZ - cubeZ);
|
||||
|
||||
if (xDiff > yDiff && xDiff > zDiff) {
|
||||
roundedX = -roundedY - roundedZ;
|
||||
} else if (yDiff > zDiff) {
|
||||
roundedY = -roundedX - roundedZ;
|
||||
} else {
|
||||
roundedZ = -roundedX - roundedY;
|
||||
}
|
||||
|
||||
return new long[]{roundedX, roundedZ};
|
||||
}
|
||||
|
||||
private double hexDistance(double q, double r, double centerQ, double centerR) {
|
||||
double deltaQ = q - centerQ;
|
||||
double deltaR = r - centerR;
|
||||
double deltaY = -deltaQ - deltaR;
|
||||
return Math.max(Math.abs(deltaQ), Math.max(Math.abs(deltaR), Math.abs(deltaY)));
|
||||
}
|
||||
|
||||
private double axialX(double q, double r) {
|
||||
return SQRT_3 * (q + (r * 0.5D));
|
||||
}
|
||||
|
||||
private double axialZ(double r) {
|
||||
return THREE_OVER_TWO * r;
|
||||
}
|
||||
|
||||
private double simplexField(SimplexNoise simplex, double x, double z, double scale) {
|
||||
if (octaves <= 1) {
|
||||
return simplex.noise(x * scale, z * scale);
|
||||
}
|
||||
|
||||
double frequency = 1D;
|
||||
double amplitude = 1D;
|
||||
double total = 0D;
|
||||
double max = 0D;
|
||||
|
||||
for (int i = 0; i < octaves; i++) {
|
||||
total += simplex.noise((x * scale) * frequency, (z * scale) * frequency) * amplitude;
|
||||
max += amplitude;
|
||||
frequency *= 2D;
|
||||
amplitude *= 0.5D;
|
||||
}
|
||||
|
||||
if (max == 0D) {
|
||||
return 0.5D;
|
||||
}
|
||||
|
||||
return clip(total / max);
|
||||
}
|
||||
|
||||
private double cellHeat(double centerQ, double centerR, long nodeHash, int level) {
|
||||
double x = axialX(centerQ, centerR);
|
||||
double z = axialZ(centerR);
|
||||
double scale = HEAT_SCALE * (1D + (level * 0.2D));
|
||||
double simplexValue = simplexField(heatSimplex, x, z, scale);
|
||||
double randomValue = random01(nodeHash, 11 + level);
|
||||
return clip((simplexValue * 0.74D) + (randomValue * 0.26D));
|
||||
}
|
||||
|
||||
private boolean shouldSubdivide(double centerQ, double centerR, long nodeHash, int level) {
|
||||
if (level >= MAX_DEPTH - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double x = axialX(centerQ, centerR);
|
||||
double z = axialZ(centerR);
|
||||
double scale = STRUCTURE_SCALE * (1D + (level * 0.31D));
|
||||
double simplexValue = simplexField(structureSimplex, x, z, scale);
|
||||
double randomValue = random01(nodeHash, 71 + level);
|
||||
double score = clip((simplexValue * 0.68D) + (randomValue * 0.32D));
|
||||
double threshold = SUBDIVIDE_BASE_THRESHOLD + (level * SUBDIVIDE_LEVEL_STEP);
|
||||
return score > threshold;
|
||||
}
|
||||
|
||||
private int pickChildIndex(double localQ, double localR, double[] centersQ, double[] centersR) {
|
||||
int bestIndex = -1;
|
||||
double bestDistance = Double.POSITIVE_INFINITY;
|
||||
|
||||
centersQ[0] = 0D;
|
||||
centersR[0] = 0D;
|
||||
double centerDistance = hexDistance(localQ, localR, 0D, 0D) / ONE_THIRD;
|
||||
|
||||
if (centerDistance <= 1D && centerDistance < bestDistance) {
|
||||
bestDistance = centerDistance;
|
||||
bestIndex = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
int index = i + 1;
|
||||
double centerQ = CHILD_DIRECTIONS[i][0] * TWO_THIRDS;
|
||||
double centerR = CHILD_DIRECTIONS[i][1] * TWO_THIRDS;
|
||||
centersQ[index] = centerQ;
|
||||
centersR[index] = centerR;
|
||||
double distance = hexDistance(localQ, localR, centerQ, centerR) / ONE_THIRD;
|
||||
|
||||
if (distance <= 1D && distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
bestIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
return bestIndex;
|
||||
}
|
||||
|
||||
private double sample(double x, double z) {
|
||||
HexScratch scratch = SCRATCH.get();
|
||||
double[] centersQ = scratch.centersQ;
|
||||
double[] centersR = scratch.centersR;
|
||||
double qWorld = (SQRT_3_OVER_3 * x) - (z / 3.0);
|
||||
double rWorld = TWO_OVER_THREE * z;
|
||||
long[] rounded = roundAxial(qWorld, rWorld);
|
||||
double centerQ = rounded[0];
|
||||
double centerR = rounded[1];
|
||||
double radius = 0.5D;
|
||||
double localQ = (qWorld - centerQ) / radius;
|
||||
double localR = (rWorld - centerR) / radius;
|
||||
long nodeHash = mix(seed ^ (rounded[0] * CONST_X) ^ (rounded[1] * CONST_Z));
|
||||
double heat = cellHeat(centerQ, centerR, nodeHash, 0);
|
||||
|
||||
for (int level = 0; level < MAX_DEPTH; level++) {
|
||||
if (!shouldSubdivide(centerQ, centerR, nodeHash, level)) {
|
||||
return heat;
|
||||
}
|
||||
|
||||
int childIndex = pickChildIndex(localQ, localR, centersQ, centersR);
|
||||
|
||||
if (childIndex < 0) {
|
||||
return heat;
|
||||
}
|
||||
|
||||
double childCenterQ = centersQ[childIndex];
|
||||
double childCenterR = centersR[childIndex];
|
||||
centerQ += childCenterQ * radius;
|
||||
centerR += childCenterR * radius;
|
||||
radius *= ONE_THIRD;
|
||||
localQ = (localQ - childCenterQ) / ONE_THIRD;
|
||||
localR = (localR - childCenterR) / ONE_THIRD;
|
||||
nodeHash = nextNodeHash(nodeHash, childIndex, level);
|
||||
heat = cellHeat(centerQ, centerR, nodeHash, level + 1);
|
||||
}
|
||||
|
||||
return heat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x) {
|
||||
return sample(x, 0D);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double z) {
|
||||
return sample(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double y, double z) {
|
||||
if (z == 0D) {
|
||||
return sample(x, y);
|
||||
}
|
||||
|
||||
double a = sample(x, z);
|
||||
double b = sample(y, x);
|
||||
double c = sample(z, y);
|
||||
return (a + b + c) / 3D;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOctaves(int o) {
|
||||
this.octaves = Math.max(1, o);
|
||||
}
|
||||
|
||||
private static final class HexScratch {
|
||||
private final double[] centersQ = new double[7];
|
||||
private final double[] centersR = new double[7];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,124 +2,27 @@ package art.arcane.iris.util.project.noise;
|
||||
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
|
||||
public class HexSimplexNoise implements NoiseGenerator, OctaveNoise {
|
||||
private static final double SQRT_3_OVER_3 = Math.sqrt(3.0) / 3.0;
|
||||
private static final double TWO_OVER_THREE = 2.0 / 3.0;
|
||||
private static final double SQRT_3 = Math.sqrt(3.0);
|
||||
private static final double THREE_OVER_TWO = 1.5D;
|
||||
private static final double CELL_HEAT_SCALE = 2.7D;
|
||||
private final SimplexNoise cellHeatSimplex;
|
||||
private int octaves;
|
||||
public class HexSimplexNoise extends HexagonalNoise implements OctaveNoise {
|
||||
private static final double HEAT_SCALE = 18D;
|
||||
private final SimplexNoise heat;
|
||||
|
||||
public HexSimplexNoise(long seed) {
|
||||
RNG rng = new RNG(seed);
|
||||
this.cellHeatSimplex = new SimplexNoise(rng.nextParallelRNG(811L).lmax());
|
||||
this.octaves = 1;
|
||||
this.heat = new SimplexNoise(new RNG(seed).nextParallelRNG(811L).lmax());
|
||||
}
|
||||
|
||||
private double clip(double value) {
|
||||
if (value < 0D) {
|
||||
return 0D;
|
||||
}
|
||||
|
||||
if (value > 1D) {
|
||||
return 1D;
|
||||
}
|
||||
|
||||
return value;
|
||||
@Override
|
||||
public void setOctaves(int octaves) {
|
||||
heat.setOctaves(octaves);
|
||||
}
|
||||
|
||||
private long[] roundAxial(double q, double r) {
|
||||
double cubeX = q;
|
||||
double cubeZ = r;
|
||||
double cubeY = -cubeX - cubeZ;
|
||||
long roundedX = Math.round(cubeX);
|
||||
long roundedY = Math.round(cubeY);
|
||||
long roundedZ = Math.round(cubeZ);
|
||||
double xDiff = Math.abs(roundedX - cubeX);
|
||||
double yDiff = Math.abs(roundedY - cubeY);
|
||||
double zDiff = Math.abs(roundedZ - cubeZ);
|
||||
|
||||
if (xDiff > yDiff && xDiff > zDiff) {
|
||||
roundedX = -roundedY - roundedZ;
|
||||
} else if (yDiff > zDiff) {
|
||||
roundedY = -roundedX - roundedZ;
|
||||
} else {
|
||||
roundedZ = -roundedX - roundedY;
|
||||
}
|
||||
|
||||
return new long[]{roundedX, roundedZ};
|
||||
}
|
||||
|
||||
private double axialX(long q, long r) {
|
||||
return SQRT_3 * (q + (r * 0.5D));
|
||||
}
|
||||
|
||||
private double axialZ(long r) {
|
||||
return THREE_OVER_TWO * r;
|
||||
}
|
||||
|
||||
private double cellHeat(long q, long r, double frequency) {
|
||||
double x = axialX(q, r);
|
||||
double z = axialZ(r);
|
||||
return cellHeatSimplex.noise((x * CELL_HEAT_SCALE) * frequency, (z * CELL_HEAT_SCALE) * frequency);
|
||||
}
|
||||
|
||||
private double sampleCell(long q, long r) {
|
||||
if (octaves <= 1) {
|
||||
return cellHeat(q, r, 1D);
|
||||
}
|
||||
|
||||
double frequency = 1D;
|
||||
double amplitude = 1D;
|
||||
double total = 0D;
|
||||
double max = 0D;
|
||||
|
||||
for (int i = 0; i < octaves; i++) {
|
||||
total += cellHeat(q, r, frequency) * amplitude;
|
||||
max += amplitude;
|
||||
frequency *= 2D;
|
||||
amplitude *= 0.5D;
|
||||
}
|
||||
|
||||
if (max == 0D) {
|
||||
return 0.5D;
|
||||
}
|
||||
|
||||
return clip(total / max);
|
||||
}
|
||||
|
||||
private double sample(double x, double z) {
|
||||
double q = (SQRT_3_OVER_3 * x) - (z / 3.0);
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
double q = (SQRT_3_OVER_3 * x) - (z / 3D);
|
||||
double r = TWO_OVER_THREE * z;
|
||||
long[] rounded = roundAxial(q, r);
|
||||
return sampleCell(rounded[0], rounded[1]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x) {
|
||||
return sample(x, 0D);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double z) {
|
||||
return sample(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double y, double z) {
|
||||
if (z == 0D) {
|
||||
return sample(x, y);
|
||||
}
|
||||
|
||||
double a = sample(x, z);
|
||||
double b = sample(y, x);
|
||||
double c = sample(z, y);
|
||||
return (a + b + c) / 3D;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOctaves(int o) {
|
||||
this.octaves = Math.max(1, o);
|
||||
long centerQ = roundQ(q, r);
|
||||
long centerR = roundR(q, r);
|
||||
double centerX = SQRT_3 * (centerQ + (centerR * 0.5D));
|
||||
double centerZ = 1.5D * centerR;
|
||||
return heat.noise(centerX * HEAT_SCALE, y * HEAT_SCALE, centerZ * HEAT_SCALE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,82 +2,27 @@ package art.arcane.iris.util.project.noise;
|
||||
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
|
||||
public class HexagonNoise implements NoiseGenerator {
|
||||
private static final double SQRT_3_OVER_3 = Math.sqrt(3.0) / 3.0;
|
||||
private static final double TWO_OVER_THREE = 2.0 / 3.0;
|
||||
private static final long CONST_X = 0x9E3779B97F4A7C15L;
|
||||
private static final long CONST_Z = 0xC2B2AE3D27D4EB4FL;
|
||||
private final long seed;
|
||||
public class HexagonNoise extends HexagonalNoise implements OctaveNoise {
|
||||
private static final double HEAT_SCALE = 18D;
|
||||
private final SimplexNoise heat;
|
||||
|
||||
public HexagonNoise(long seed) {
|
||||
this.seed = new RNG(seed).lmax();
|
||||
this.heat = new SimplexNoise(new RNG(seed).lmax());
|
||||
}
|
||||
|
||||
private long mix(long input) {
|
||||
input = (input ^ (input >>> 33)) * 0xff51afd7ed558ccdL;
|
||||
input = (input ^ (input >>> 33)) * 0xc4ceb9fe1a85ec53L;
|
||||
return input ^ (input >>> 33);
|
||||
@Override
|
||||
public void setOctaves(int octaves) {
|
||||
heat.setOctaves(octaves);
|
||||
}
|
||||
|
||||
private long hash(long q, long r) {
|
||||
long hash = seed;
|
||||
hash ^= mix(q + CONST_X);
|
||||
hash ^= mix(r + CONST_Z);
|
||||
return mix(hash);
|
||||
}
|
||||
|
||||
private long[] roundAxial(double q, double r) {
|
||||
double cubeX = q;
|
||||
double cubeZ = r;
|
||||
double cubeY = -cubeX - cubeZ;
|
||||
|
||||
long rx = Math.round(cubeX);
|
||||
long ry = Math.round(cubeY);
|
||||
long rz = Math.round(cubeZ);
|
||||
|
||||
double xDiff = Math.abs(rx - cubeX);
|
||||
double yDiff = Math.abs(ry - cubeY);
|
||||
double zDiff = Math.abs(rz - cubeZ);
|
||||
|
||||
if (xDiff > yDiff && xDiff > zDiff) {
|
||||
rx = -ry - rz;
|
||||
} else if (yDiff > zDiff) {
|
||||
ry = -rx - rz;
|
||||
} else {
|
||||
rz = -rx - ry;
|
||||
}
|
||||
|
||||
return new long[]{rx, rz};
|
||||
}
|
||||
|
||||
private double sampleCell(double x, double z) {
|
||||
double q = (SQRT_3_OVER_3 * x) - (z / 3.0);
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
double q = (SQRT_3_OVER_3 * x) - (z / 3D);
|
||||
double r = TWO_OVER_THREE * z;
|
||||
long[] axial = roundAxial(q, r);
|
||||
long hashed = hash(axial[0], axial[1]);
|
||||
long normalized = hashed & Long.MAX_VALUE;
|
||||
return normalized / (double) Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x) {
|
||||
return sampleCell(x, 0D);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double z) {
|
||||
return sampleCell(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double y, double z) {
|
||||
if (z == 0D) {
|
||||
return sampleCell(x, y);
|
||||
}
|
||||
|
||||
double a = sampleCell(x, z);
|
||||
double b = sampleCell(y, x);
|
||||
double c = sampleCell(z, y);
|
||||
return (a + b + c) / 3D;
|
||||
long centerQ = roundQ(q, r);
|
||||
long centerR = roundR(q, r);
|
||||
double centerX = SQRT_3 * (centerQ + (centerR * 0.5D));
|
||||
double centerZ = 1.5D * centerR;
|
||||
return heat.noise(centerX * HEAT_SCALE, y * HEAT_SCALE, centerZ * HEAT_SCALE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
abstract class HexagonalNoise implements NoiseGenerator {
|
||||
protected static final double SQRT_3 = Math.sqrt(3D);
|
||||
protected static final double SQRT_3_OVER_3 = SQRT_3 / 3D;
|
||||
protected static final double TWO_OVER_THREE = 2D / 3D;
|
||||
protected static final double ONE_THIRD = 1D / 3D;
|
||||
protected static final double[] CHILD_Q = {0D, 4D / 9D, 2D / 9D, -2D / 9D, -4D / 9D, -2D / 9D, 2D / 9D};
|
||||
protected static final double[] CHILD_R = {0D, -2D / 9D, 2D / 9D, 4D / 9D, 2D / 9D, -2D / 9D, -4D / 9D};
|
||||
protected static final long CONST_X = 0x9E3779B97F4A7C15L;
|
||||
protected static final long CONST_Z = 0xC2B2AE3D27D4EB4FL;
|
||||
|
||||
@Override
|
||||
public double noise(double x) {
|
||||
return sample(x, 0D, 0D);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double z) {
|
||||
return sample(x, 0D, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double y, double z) {
|
||||
return sample(x, y, z);
|
||||
}
|
||||
|
||||
protected abstract double sample(double x, double y, double z);
|
||||
|
||||
protected static long roundQ(double q, double r) {
|
||||
double cubeY = -q - r;
|
||||
long roundedQ = Math.round(q);
|
||||
long roundedR = Math.round(r);
|
||||
long roundedY = Math.round(cubeY);
|
||||
double qDiff = Math.abs(roundedQ - q);
|
||||
double rDiff = Math.abs(roundedR - r);
|
||||
double yDiff = Math.abs(roundedY - cubeY);
|
||||
return qDiff > yDiff && qDiff > rDiff ? -roundedY - roundedR : roundedQ;
|
||||
}
|
||||
|
||||
protected static long roundR(double q, double r) {
|
||||
double cubeY = -q - r;
|
||||
long roundedQ = Math.round(q);
|
||||
long roundedR = Math.round(r);
|
||||
long roundedY = Math.round(cubeY);
|
||||
double qDiff = Math.abs(roundedQ - q);
|
||||
double rDiff = Math.abs(roundedR - r);
|
||||
double yDiff = Math.abs(roundedY - cubeY);
|
||||
return !(qDiff > yDiff && qDiff > rDiff) && yDiff <= rDiff ? -roundedQ - roundedY : roundedR;
|
||||
}
|
||||
|
||||
protected static long mix(long input) {
|
||||
input = (input ^ (input >>> 33)) * 0xff51afd7ed558ccdL;
|
||||
input = (input ^ (input >>> 33)) * 0xc4ceb9fe1a85ec53L;
|
||||
return input ^ (input >>> 33);
|
||||
}
|
||||
|
||||
protected static int pickChildIndex(double localQ, double localR) {
|
||||
for (int index = 0; index < CHILD_Q.length; index++) {
|
||||
double deltaQ = localQ - CHILD_Q[index];
|
||||
double deltaR = localR - CHILD_R[index];
|
||||
double distance = Math.max(Math.abs((2D * deltaQ) + deltaR),
|
||||
Math.max(Math.abs(deltaQ + (2D * deltaR)), Math.abs(deltaQ - deltaR)));
|
||||
if (distance <= ONE_THIRD) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -21,14 +21,16 @@ package art.arcane.iris.util.project.noise;
|
||||
import art.arcane.volmlib.util.function.NoiseProvider;
|
||||
import art.arcane.iris.util.project.interpolation.InterpolationMethod;
|
||||
import art.arcane.iris.util.project.interpolation.IrisInterpolation;
|
||||
public class InterpolatedNoise implements NoiseGenerator {
|
||||
public class InterpolatedNoise implements NoiseGenerator, OctaveNoise {
|
||||
private final InterpolationMethod method;
|
||||
private final NoiseGenerator generator;
|
||||
private final NoiseProvider p;
|
||||
|
||||
public InterpolatedNoise(long seed, NoiseType type, InterpolationMethod method) {
|
||||
this.method = method;
|
||||
NoiseGenerator g = type.create(seed);
|
||||
p = g::noise;
|
||||
generator = type.create(seed);
|
||||
double coordinateScale = type.getCoordinateScale();
|
||||
p = (x, z) -> generator.noise(x * coordinateScale, z * coordinateScale);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -38,15 +40,18 @@ public class InterpolatedNoise implements NoiseGenerator {
|
||||
|
||||
@Override
|
||||
public double noise(double x, double z) {
|
||||
return IrisInterpolation.getNoise(method, (int) x, (int) z, 32, p);
|
||||
return Math.max(0D, Math.min(1D, IrisInterpolation.getNoise(method, x, z, 32, p)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double y, double z) {
|
||||
if (z == 0) {
|
||||
return noise(x, y);
|
||||
}
|
||||
return noise(x, z);
|
||||
}
|
||||
|
||||
return IrisInterpolation.getNoise(method, (int) x, (int) z, 32, p);
|
||||
@Override
|
||||
public void setOctaves(int octaves) {
|
||||
if (generator instanceof OctaveNoise octaveNoise) {
|
||||
octaveNoise.setOctaves(octaves);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
public final class KaleidoscopeNoise extends PatternNoise {
|
||||
private static final double TAU = Math.PI * 2D;
|
||||
|
||||
private final long seed;
|
||||
|
||||
public KaleidoscopeNoise(long seed) {
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
z += 0.5D;
|
||||
long cellX = (long) Math.floor(x);
|
||||
long cellZ = (long) Math.floor(z);
|
||||
double dx = x - cellX - 0.5D;
|
||||
double dz = z - cellZ - 0.5D;
|
||||
double radiusSquared = dx * dx + dz * dz;
|
||||
if (radiusSquared >= 0.25D) {
|
||||
return 0D;
|
||||
}
|
||||
|
||||
long hash = mix(seed ^ cellX * 0x9E3779B97F4A7C15L ^ cellZ * 0xD1B54A32D192ED03L);
|
||||
int wedges = 4 + (int) (hash & 3L);
|
||||
double phase = (hash >>> 11) * 0x1.0p-53D * TAU;
|
||||
double sector = TAU / wedges;
|
||||
double angle = Math.atan2(dz, dx) + phase + y * 0.35D;
|
||||
angle = Math.abs(angle - Math.floor(angle / sector + 0.5D) * sector);
|
||||
double radius = Math.sqrt(radiusSquared);
|
||||
double u = radius * Math.cos(angle);
|
||||
double v = radius * Math.sin(angle);
|
||||
double motif = 0.5D + 0.5D * Math.cos(16D * u + 2.5D * Math.cos(18D * v + phase + y));
|
||||
double envelope = 1D - radiusSquared * 4D;
|
||||
return motif * envelope * envelope;
|
||||
}
|
||||
|
||||
private static long mix(long value) {
|
||||
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
|
||||
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
|
||||
return value ^ (value >>> 31);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import java.util.SplittableRandom;
|
||||
|
||||
public final class MarbleNoise extends PatternNoise {
|
||||
private static final double TAU = Math.PI * 2D;
|
||||
|
||||
private final FastNoiseDouble distortion;
|
||||
private final double directionX;
|
||||
private final double directionY;
|
||||
private final double directionZ;
|
||||
private final double phase;
|
||||
|
||||
public MarbleNoise(long seed) {
|
||||
SplittableRandom random = new SplittableRandom(seed);
|
||||
distortion = new FastNoiseDouble(random.nextLong());
|
||||
distortion.setFrequency(1D);
|
||||
double angle = random.nextDouble(TAU);
|
||||
directionX = Math.cos(angle);
|
||||
directionY = random.nextDouble(0.25D, 0.65D);
|
||||
directionZ = Math.sin(angle);
|
||||
phase = random.nextDouble(TAU);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
double broad = distortion.GetSimplex(x * 0.45D, y * 0.45D, z * 0.45D);
|
||||
double detail = distortion.GetSimplex(x * 1.3D + 17.3D, y * 1.3D - 11.7D, z * 1.3D + 5.9D);
|
||||
double warp = broad * 1.1D + detail * 0.2D;
|
||||
double position = x * directionX + y * directionY + z * directionZ;
|
||||
double ridge = Math.sin(TAU * (position + warp) + phase);
|
||||
double vein = 1D / (1D + 90D * ridge * ridge);
|
||||
double stone = 0.85D + 0.15D * broad;
|
||||
return Math.clamp(0.1D + 0.84D * stone * (1D - vein), 0D, 1D);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
public final class MengerSpongeNoise extends PatternNoise {
|
||||
private static final int DEPTH = 3;
|
||||
|
||||
private final double phaseX;
|
||||
private final double phaseY;
|
||||
private final double phaseZ;
|
||||
|
||||
public MengerSpongeNoise(long seed) {
|
||||
long hash = mix(seed);
|
||||
phaseX = (hash & 0x1FFFFFL) * 0x1.0p-21D;
|
||||
phaseY = ((hash >>> 21) & 0x1FFFFFL) * (0x1.0p-21D / 3D);
|
||||
phaseZ = (hash >>> 42) * 0x1.0p-22D;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
x += phaseX;
|
||||
y += phaseY;
|
||||
z += phaseZ;
|
||||
x -= Math.floor(x);
|
||||
y -= Math.floor(y);
|
||||
z -= Math.floor(z);
|
||||
for (int level = 0; level < DEPTH; level++) {
|
||||
x *= 3D;
|
||||
y *= 3D;
|
||||
z *= 3D;
|
||||
int cellX = (int) x;
|
||||
int cellY = (int) y;
|
||||
int cellZ = (int) z;
|
||||
int middleAxes = (cellX == 1 ? 1 : 0) + (cellY == 1 ? 1 : 0) + (cellZ == 1 ? 1 : 0);
|
||||
if (middleAxes >= 2) {
|
||||
return 0D;
|
||||
}
|
||||
x -= cellX;
|
||||
y -= cellY;
|
||||
z -= cellZ;
|
||||
}
|
||||
return 1D;
|
||||
}
|
||||
|
||||
private static long mix(long value) {
|
||||
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
|
||||
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
|
||||
return value ^ (value >>> 31);
|
||||
}
|
||||
}
|
||||
@@ -20,79 +20,104 @@ package art.arcane.iris.util.project.noise;
|
||||
|
||||
import art.arcane.iris.util.project.interpolation.InterpolationMethod;
|
||||
public enum NoiseType {
|
||||
WHITE(WhiteNoise::new),
|
||||
WHITE_BILINEAR((s) -> new InterpolatedNoise(s, WHITE, InterpolationMethod.BILINEAR)),
|
||||
WHITE_BICUBIC((s) -> new InterpolatedNoise(s, WHITE, InterpolationMethod.BICUBIC)),
|
||||
WHITE_HERMITE((s) -> new InterpolatedNoise(s, WHITE, InterpolationMethod.HERMITE)),
|
||||
WHITE(WhiteNoise::new, 1D),
|
||||
WHITE_BILINEAR((s) -> new InterpolatedNoise(s, WHITE, InterpolationMethod.BILINEAR), 1D),
|
||||
WHITE_BICUBIC((s) -> new InterpolatedNoise(s, WHITE, InterpolationMethod.BICUBIC), 1D),
|
||||
WHITE_HERMITE((s) -> new InterpolatedNoise(s, WHITE, InterpolationMethod.HERMITE), 1D),
|
||||
SIMPLEX(SimplexNoise::new),
|
||||
PERLIN(seed -> new PerlinNoise(seed).hermite()),
|
||||
PERLIN(PerlinNoise::new),
|
||||
FRACTAL_BILLOW_SIMPLEX(FractalBillowSimplexNoise::new),
|
||||
FRACTAL_BILLOW_PERLIN(FractalBillowPerlinNoise::new),
|
||||
FRACTAL_FBM_SIMPLEX(FractalFBMSimplexNoise::new),
|
||||
FRACTAL_RIGID_MULTI_SIMPLEX(FractalRigidMultiSimplexNoise::new),
|
||||
FLAT(FlatNoise::new),
|
||||
FLAT(FlatNoise::new, 1D),
|
||||
CELLULAR(CellularNoise::new),
|
||||
CELLULAR_BILINEAR((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.BILINEAR)),
|
||||
CELLULAR_BILINEAR_STARCAST_3((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.BILINEAR_STARCAST_3)),
|
||||
CELLULAR_BILINEAR_STARCAST_6((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.BILINEAR_STARCAST_6)),
|
||||
CELLULAR_BILINEAR_STARCAST_9((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.BILINEAR_STARCAST_9)),
|
||||
CELLULAR_BILINEAR_STARCAST_12((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.BILINEAR_STARCAST_12)),
|
||||
CELLULAR_BICUBIC((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.BICUBIC)),
|
||||
CELLULAR_HERMITE((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.HERMITE)),
|
||||
CELLULAR_STARCAST_3((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.STARCAST_3)),
|
||||
CELLULAR_STARCAST_6((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.STARCAST_6)),
|
||||
CELLULAR_STARCAST_9((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.STARCAST_9)),
|
||||
CELLULAR_STARCAST_12((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.STARCAST_12)),
|
||||
CELLULAR_HERMITE_STARCAST_3((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.HERMITE_STARCAST_3)),
|
||||
CELLULAR_HERMITE_STARCAST_6((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.HERMITE_STARCAST_6)),
|
||||
CELLULAR_HERMITE_STARCAST_9((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.HERMITE_STARCAST_9)),
|
||||
CELLULAR_HERMITE_STARCAST_12((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.HERMITE_STARCAST_12)),
|
||||
CELLULAR_BILINEAR((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.BILINEAR), 1D),
|
||||
CELLULAR_BILINEAR_STARCAST_3((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.BILINEAR_STARCAST_3), 1D),
|
||||
CELLULAR_BILINEAR_STARCAST_6((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.BILINEAR_STARCAST_6), 1D),
|
||||
CELLULAR_BILINEAR_STARCAST_9((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.BILINEAR_STARCAST_9), 1D),
|
||||
CELLULAR_BILINEAR_STARCAST_12((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.BILINEAR_STARCAST_12), 1D),
|
||||
CELLULAR_BICUBIC((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.BICUBIC), 1D),
|
||||
CELLULAR_HERMITE((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.HERMITE), 1D),
|
||||
CELLULAR_STARCAST_3((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.STARCAST_3), 1D),
|
||||
CELLULAR_STARCAST_6((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.STARCAST_6), 1D),
|
||||
CELLULAR_STARCAST_9((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.STARCAST_9), 1D),
|
||||
CELLULAR_STARCAST_12((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.STARCAST_12), 1D),
|
||||
CELLULAR_HERMITE_STARCAST_3((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.HERMITE_STARCAST_3), 1D),
|
||||
CELLULAR_HERMITE_STARCAST_6((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.HERMITE_STARCAST_6), 1D),
|
||||
CELLULAR_HERMITE_STARCAST_9((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.HERMITE_STARCAST_9), 1D),
|
||||
CELLULAR_HERMITE_STARCAST_12((s) -> new InterpolatedNoise(s, CELLULAR, InterpolationMethod.HERMITE_STARCAST_12), 1D),
|
||||
GLOB(GlobNoise::new),
|
||||
CUBIC(CubicNoise::new),
|
||||
FRACTAL_CUBIC(FractalCubicNoise::new),
|
||||
CELLULAR_HEIGHT(CellHeightNoise::new),
|
||||
CLOVER(CloverNoise::new),
|
||||
CLOVER_BILINEAR((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.BILINEAR)),
|
||||
CLOVER_BILINEAR_STARCAST_3((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.BILINEAR_STARCAST_3)),
|
||||
CLOVER_BILINEAR_STARCAST_6((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.BILINEAR_STARCAST_6)),
|
||||
CLOVER_BILINEAR_STARCAST_9((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.BILINEAR_STARCAST_9)),
|
||||
CLOVER_BILINEAR_STARCAST_12((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.BILINEAR_STARCAST_12)),
|
||||
CLOVER_BICUBIC((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.BICUBIC)),
|
||||
CLOVER_HERMITE((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.HERMITE)),
|
||||
CLOVER_STARCAST_3((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.STARCAST_3)),
|
||||
CLOVER_STARCAST_6((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.STARCAST_6)),
|
||||
CLOVER_STARCAST_9((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.STARCAST_9)),
|
||||
CLOVER_STARCAST_12((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.STARCAST_12)),
|
||||
CLOVER_HERMITE_STARCAST_3((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.HERMITE_STARCAST_3)),
|
||||
CLOVER_HERMITE_STARCAST_6((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.HERMITE_STARCAST_6)),
|
||||
CLOVER_HERMITE_STARCAST_9((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.HERMITE_STARCAST_9)),
|
||||
CLOVER_HERMITE_STARCAST_12((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.HERMITE_STARCAST_12)),
|
||||
HEXAGON(HexagonNoise::new),
|
||||
HEXAGON_BILINEAR((s) -> new InterpolatedNoise(s, HEXAGON, InterpolationMethod.BILINEAR)),
|
||||
HEXAGON_BICUBIC((s) -> new InterpolatedNoise(s, HEXAGON, InterpolationMethod.BICUBIC)),
|
||||
HEXAGON_HERMITE((s) -> new InterpolatedNoise(s, HEXAGON, InterpolationMethod.HERMITE)),
|
||||
HEX_JAMES(HexJamesNoise::new),
|
||||
HEX_JAMES_BILINEAR((s) -> new InterpolatedNoise(s, HEX_JAMES, InterpolationMethod.BILINEAR)),
|
||||
HEX_JAMES_BICUBIC((s) -> new InterpolatedNoise(s, HEX_JAMES, InterpolationMethod.BICUBIC)),
|
||||
HEX_JAMES_HERMITE((s) -> new InterpolatedNoise(s, HEX_JAMES, InterpolationMethod.HERMITE)),
|
||||
HEX_SIMPLEX(HexSimplexNoise::new),
|
||||
HEX_SIMPLEX_BILINEAR((s) -> new InterpolatedNoise(s, HEX_SIMPLEX, InterpolationMethod.BILINEAR)),
|
||||
HEX_SIMPLEX_BICUBIC((s) -> new InterpolatedNoise(s, HEX_SIMPLEX, InterpolationMethod.BICUBIC)),
|
||||
HEX_SIMPLEX_HERMITE((s) -> new InterpolatedNoise(s, HEX_SIMPLEX, InterpolationMethod.HERMITE)),
|
||||
HEX_RANDOM_SIZE(HexRandomSizeNoise::new),
|
||||
HEX_RANDOM_SIZE_BILINEAR((s) -> new InterpolatedNoise(s, HEX_RANDOM_SIZE, InterpolationMethod.BILINEAR)),
|
||||
HEX_RANDOM_SIZE_BICUBIC((s) -> new InterpolatedNoise(s, HEX_RANDOM_SIZE, InterpolationMethod.BICUBIC)),
|
||||
HEX_RANDOM_SIZE_HERMITE((s) -> new InterpolatedNoise(s, HEX_RANDOM_SIZE, InterpolationMethod.HERMITE)),
|
||||
SIERPINSKI_TRIANGLE(SierpinskiTriangleNoise::new),
|
||||
SIERPINSKI_TRIANGLE_BILINEAR((s) -> new InterpolatedNoise(s, SIERPINSKI_TRIANGLE, InterpolationMethod.BILINEAR)),
|
||||
SIERPINSKI_TRIANGLE_BICUBIC((s) -> new InterpolatedNoise(s, SIERPINSKI_TRIANGLE, InterpolationMethod.BICUBIC)),
|
||||
SIERPINSKI_TRIANGLE_HERMITE((s) -> new InterpolatedNoise(s, SIERPINSKI_TRIANGLE, InterpolationMethod.HERMITE)),
|
||||
VASCULAR(VascularNoise::new);
|
||||
CLOVER(CloverNoise::new, 1D / 64D),
|
||||
CLOVER_BILINEAR((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.BILINEAR), 1D),
|
||||
CLOVER_BILINEAR_STARCAST_3((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.BILINEAR_STARCAST_3), 1D),
|
||||
CLOVER_BILINEAR_STARCAST_6((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.BILINEAR_STARCAST_6), 1D),
|
||||
CLOVER_BILINEAR_STARCAST_9((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.BILINEAR_STARCAST_9), 1D),
|
||||
CLOVER_BILINEAR_STARCAST_12((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.BILINEAR_STARCAST_12), 1D),
|
||||
CLOVER_BICUBIC((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.BICUBIC), 1D),
|
||||
CLOVER_HERMITE((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.HERMITE), 1D),
|
||||
CLOVER_STARCAST_3((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.STARCAST_3), 1D),
|
||||
CLOVER_STARCAST_6((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.STARCAST_6), 1D),
|
||||
CLOVER_STARCAST_9((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.STARCAST_9), 1D),
|
||||
CLOVER_STARCAST_12((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.STARCAST_12), 1D),
|
||||
CLOVER_HERMITE_STARCAST_3((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.HERMITE_STARCAST_3), 1D),
|
||||
CLOVER_HERMITE_STARCAST_6((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.HERMITE_STARCAST_6), 1D),
|
||||
CLOVER_HERMITE_STARCAST_9((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.HERMITE_STARCAST_9), 1D),
|
||||
CLOVER_HERMITE_STARCAST_12((s) -> new InterpolatedNoise(s, CLOVER, InterpolationMethod.HERMITE_STARCAST_12), 1D),
|
||||
HEXAGON(HexagonNoise::new, 2D / 64D),
|
||||
HEXAGON_BILINEAR((s) -> new InterpolatedNoise(s, HEXAGON, InterpolationMethod.BILINEAR), 1D),
|
||||
HEXAGON_BICUBIC((s) -> new InterpolatedNoise(s, HEXAGON, InterpolationMethod.BICUBIC), 1D),
|
||||
HEXAGON_HERMITE((s) -> new InterpolatedNoise(s, HEXAGON, InterpolationMethod.HERMITE), 1D),
|
||||
HEX_JAMES(HexJamesNoise::new, 2D / 64D),
|
||||
HEX_JAMES_BILINEAR((s) -> new InterpolatedNoise(s, HEX_JAMES, InterpolationMethod.BILINEAR), 1D),
|
||||
HEX_JAMES_BICUBIC((s) -> new InterpolatedNoise(s, HEX_JAMES, InterpolationMethod.BICUBIC), 1D),
|
||||
HEX_JAMES_HERMITE((s) -> new InterpolatedNoise(s, HEX_JAMES, InterpolationMethod.HERMITE), 1D),
|
||||
HEX_SIMPLEX(HexSimplexNoise::new, 2D / 64D),
|
||||
HEX_SIMPLEX_BILINEAR((s) -> new InterpolatedNoise(s, HEX_SIMPLEX, InterpolationMethod.BILINEAR), 1D),
|
||||
HEX_SIMPLEX_BICUBIC((s) -> new InterpolatedNoise(s, HEX_SIMPLEX, InterpolationMethod.BICUBIC), 1D),
|
||||
HEX_SIMPLEX_HERMITE((s) -> new InterpolatedNoise(s, HEX_SIMPLEX, InterpolationMethod.HERMITE), 1D),
|
||||
HEX_RANDOM_SIZE(HexRandomSizeNoise::new, 2D / 64D),
|
||||
HEX_RANDOM_SIZE_BILINEAR((s) -> new InterpolatedNoise(s, HEX_RANDOM_SIZE, InterpolationMethod.BILINEAR), 1D),
|
||||
HEX_RANDOM_SIZE_BICUBIC((s) -> new InterpolatedNoise(s, HEX_RANDOM_SIZE, InterpolationMethod.BICUBIC), 1D),
|
||||
HEX_RANDOM_SIZE_HERMITE((s) -> new InterpolatedNoise(s, HEX_RANDOM_SIZE, InterpolationMethod.HERMITE), 1D),
|
||||
SIERPINSKI_TRIANGLE(SierpinskiTriangleNoise::new, 8D / 64D),
|
||||
SIERPINSKI_TRIANGLE_BILINEAR((s) -> new InterpolatedNoise(s, SIERPINSKI_TRIANGLE, InterpolationMethod.BILINEAR), 1D),
|
||||
SIERPINSKI_TRIANGLE_BICUBIC((s) -> new InterpolatedNoise(s, SIERPINSKI_TRIANGLE, InterpolationMethod.BICUBIC), 1D),
|
||||
SIERPINSKI_TRIANGLE_HERMITE((s) -> new InterpolatedNoise(s, SIERPINSKI_TRIANGLE, InterpolationMethod.HERMITE), 1D),
|
||||
VASCULAR(VascularNoise::new),
|
||||
GYROID(GyroidNoise::new, 1D / 64D),
|
||||
QUASICRYSTAL(QuasicrystalNoise::new, 1D / 64D),
|
||||
TRUCHET(TruchetNoise::new, 1D / 64D),
|
||||
CRATER(CraterNoise::new, 1D / 64D),
|
||||
VORTEX(VortexNoise::new, 1D / 64D),
|
||||
DUNE(DuneNoise::new, 1D / 64D),
|
||||
STRATA(StrataNoise::new, 1D / 64D),
|
||||
WOOD(WoodNoise::new, 1D / 64D),
|
||||
GABOR(GaborNoise::new, 1D / 64D),
|
||||
MARBLE(MarbleNoise::new, 1D / 64D),
|
||||
SCALES(ScalesNoise::new, 1D / 64D),
|
||||
CHLADNI(ChladniNoise::new, 1D / 64D),
|
||||
KALEIDOSCOPE(KaleidoscopeNoise::new, 1D / 64D),
|
||||
MENGER_SPONGE(MengerSpongeNoise::new, 1D / 64D),
|
||||
CIRCUIT(CircuitNoise::new, 1D / 64D);
|
||||
|
||||
private final NoiseFactory f;
|
||||
private final double coordinateScale;
|
||||
|
||||
NoiseType(NoiseFactory f) {
|
||||
this(f, 100D / 64D);
|
||||
}
|
||||
|
||||
NoiseType(NoiseFactory f, double coordinateScale) {
|
||||
this.f = f;
|
||||
this.coordinateScale = coordinateScale;
|
||||
}
|
||||
|
||||
public double getCoordinateScale() {
|
||||
return coordinateScale;
|
||||
}
|
||||
|
||||
public NoiseGenerator create(long seed) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import art.arcane.volmlib.util.math.RNG;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
public class OffsetNoiseGenerator implements NoiseGenerator {
|
||||
public class OffsetNoiseGenerator implements NoiseGenerator, OctaveNoise {
|
||||
private static final int GENERIC = 0;
|
||||
private static final int SIMPLEX = 1;
|
||||
private static final int PERLIN = 2;
|
||||
@@ -102,6 +102,13 @@ public class OffsetNoiseGenerator implements NoiseGenerator {
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOctaves(int octaves) {
|
||||
if (base instanceof OctaveNoise octaveNoise) {
|
||||
octaveNoise.setOctaves(octaves);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNoScale() {
|
||||
return base.isNoScale();
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
abstract class PatternNoise implements NoiseGenerator, OctaveNoise {
|
||||
private int octaves = 1;
|
||||
private double bounding = 1D;
|
||||
|
||||
@Override
|
||||
public final double noise(double x) {
|
||||
return noise(x, 0D);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final double noise(double x, double z) {
|
||||
return noise(x, 0D, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final double noise(double x, double y, double z) {
|
||||
if (octaves == 1) {
|
||||
return sample(x, y, z);
|
||||
}
|
||||
double sum = 0D;
|
||||
double frequency = 1D;
|
||||
double amplitude = 1D;
|
||||
for (int octave = 0; octave < octaves; octave++) {
|
||||
sum += sample(x * frequency, y * frequency, z * frequency) * amplitude;
|
||||
frequency *= 2D;
|
||||
amplitude *= 0.5D;
|
||||
}
|
||||
return sum * bounding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setOctaves(int octaves) {
|
||||
this.octaves = Math.clamp(octaves, 1, 16);
|
||||
bounding = 1D / (2D - Math.scalb(1D, 1 - this.octaves));
|
||||
}
|
||||
|
||||
protected abstract double sample(double x, double y, double z);
|
||||
}
|
||||
@@ -23,9 +23,11 @@ import art.arcane.volmlib.util.math.RNG;
|
||||
public class PerlinNoise implements NoiseGenerator, OctaveNoise {
|
||||
private final FastNoiseDouble n;
|
||||
private int octaves;
|
||||
private double octaveBounding = 1D;
|
||||
|
||||
public PerlinNoise(long seed) {
|
||||
this.n = new FastNoiseDouble(new RNG(seed).lmax());
|
||||
n.setLongerp(FastNoiseDouble.Longerp.Qulongic);
|
||||
octaves = 1;
|
||||
}
|
||||
|
||||
@@ -35,128 +37,74 @@ public class PerlinNoise implements NoiseGenerator, OctaveNoise {
|
||||
|
||||
@Override
|
||||
public double noise(double x) {
|
||||
if (octaves <= 1) {
|
||||
return f(n.GetPerlin(x, 0));
|
||||
}
|
||||
|
||||
double f = 1;
|
||||
double m = 0;
|
||||
double v = 0;
|
||||
|
||||
for (int i = 0; i < octaves; i++) {
|
||||
v += n.GetPerlin((x * (f == 1 ? f++ : (f *= 2))), 0) * f;
|
||||
m += f;
|
||||
}
|
||||
|
||||
return f(v / m);
|
||||
return f(noiseSigned(x));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noiseSigned(double x) {
|
||||
if (octaves <= 1) {
|
||||
if (octaves == 1) {
|
||||
return n.GetPerlin(x, 0D);
|
||||
}
|
||||
|
||||
double frequency = 1D;
|
||||
double magnitude = 0D;
|
||||
double amplitude = 1D;
|
||||
double value = 0D;
|
||||
|
||||
for (int i = 0; i < octaves; i++) {
|
||||
double sampleFrequency;
|
||||
if (frequency == 1D) {
|
||||
sampleFrequency = frequency;
|
||||
frequency = 2D;
|
||||
} else {
|
||||
frequency *= 2D;
|
||||
sampleFrequency = frequency;
|
||||
}
|
||||
|
||||
value += n.GetPerlin(x * sampleFrequency, 0D) * frequency;
|
||||
magnitude += frequency;
|
||||
value += n.GetPerlin(x * frequency, 0D) * amplitude;
|
||||
frequency *= 2D;
|
||||
amplitude *= 0.5D;
|
||||
}
|
||||
|
||||
return value / magnitude;
|
||||
return value * octaveBounding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double z) {
|
||||
if (octaves <= 1) {
|
||||
return f(n.GetPerlin(x, z));
|
||||
}
|
||||
double f = 1;
|
||||
double m = 0;
|
||||
double v = 0;
|
||||
|
||||
for (int i = 0; i < octaves; i++) {
|
||||
f = f == 1 ? f + 1 : f * 2;
|
||||
v += n.GetPerlin((x * f), (z * f)) * f;
|
||||
m += f;
|
||||
}
|
||||
|
||||
return f(v / m);
|
||||
return f(noiseSigned(x, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noiseSigned(double x, double z) {
|
||||
if (octaves <= 1) {
|
||||
if (octaves == 1) {
|
||||
return n.GetPerlin(x, z);
|
||||
}
|
||||
|
||||
double frequency = 1D;
|
||||
double magnitude = 0D;
|
||||
double amplitude = 1D;
|
||||
double value = 0D;
|
||||
|
||||
for (int i = 0; i < octaves; i++) {
|
||||
double octaveFrequency = frequency == 1D ? frequency + 1D : frequency * 2D;
|
||||
value += n.GetPerlin(x * octaveFrequency, z * octaveFrequency) * octaveFrequency;
|
||||
magnitude += octaveFrequency;
|
||||
frequency = octaveFrequency;
|
||||
value += n.GetPerlin(x * frequency, z * frequency) * amplitude;
|
||||
frequency *= 2D;
|
||||
amplitude *= 0.5D;
|
||||
}
|
||||
|
||||
return value / magnitude;
|
||||
return value * octaveBounding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double y, double z) {
|
||||
if (octaves <= 1) {
|
||||
return f(n.GetPerlin(x, y, z));
|
||||
}
|
||||
double f = 1;
|
||||
double m = 0;
|
||||
double v = 0;
|
||||
|
||||
for (int i = 0; i < octaves; i++) {
|
||||
f = f == 1 ? f + 1 : f * 2;
|
||||
v += n.GetPerlin((x * f), (y * f), (z * f)) * f;
|
||||
m += f;
|
||||
}
|
||||
|
||||
return f(v / m);
|
||||
return f(noiseSigned(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noiseSigned(double x, double y, double z) {
|
||||
if (octaves <= 1) {
|
||||
if (octaves == 1) {
|
||||
return n.GetPerlin(x, y, z);
|
||||
}
|
||||
|
||||
double frequency = 1D;
|
||||
double magnitude = 0D;
|
||||
double amplitude = 1D;
|
||||
double value = 0D;
|
||||
|
||||
for (int i = 0; i < octaves; i++) {
|
||||
double octaveFrequency = frequency == 1D ? frequency + 1D : frequency * 2D;
|
||||
value += n.GetPerlin(x * octaveFrequency, y * octaveFrequency, z * octaveFrequency) * octaveFrequency;
|
||||
magnitude += octaveFrequency;
|
||||
frequency = octaveFrequency;
|
||||
value += n.GetPerlin(x * frequency, y * frequency, z * frequency) * amplitude;
|
||||
frequency *= 2D;
|
||||
amplitude *= 0.5D;
|
||||
}
|
||||
|
||||
return value / magnitude;
|
||||
return value * octaveBounding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOctaves(int o) {
|
||||
octaves = o;
|
||||
public void setOctaves(int octaves) {
|
||||
this.octaves = Math.max(1, Math.min(16, octaves));
|
||||
octaveBounding = 1D / (2D - Math.scalb(1D, 1 - this.octaves));
|
||||
}
|
||||
|
||||
public NoiseGenerator hermite() {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import java.util.SplittableRandom;
|
||||
|
||||
public final class QuasicrystalNoise extends PatternNoise {
|
||||
private static final int WAVES = 5;
|
||||
private static final double TAU = Math.PI * 2D;
|
||||
|
||||
private final double[] waveX = new double[WAVES];
|
||||
private final double[] waveZ = new double[WAVES];
|
||||
private final double phase;
|
||||
private final double verticalFrequency;
|
||||
private final double contourPhase;
|
||||
|
||||
public QuasicrystalNoise(long seed) {
|
||||
SplittableRandom random = new SplittableRandom(seed);
|
||||
double rotation = random.nextDouble(TAU);
|
||||
for (int i = 0; i < WAVES; i++) {
|
||||
double angle = rotation + i * TAU / WAVES;
|
||||
waveX[i] = Math.cos(angle) * TAU;
|
||||
waveZ[i] = Math.sin(angle) * TAU;
|
||||
}
|
||||
phase = random.nextDouble(TAU);
|
||||
verticalFrequency = random.nextDouble(0.3D, 0.7D) * TAU;
|
||||
contourPhase = random.nextDouble(TAU);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
double layerPhase = phase + y * verticalFrequency;
|
||||
double sum = 0D;
|
||||
for (int i = 0; i < WAVES; i++) {
|
||||
sum += Math.cos(x * waveX[i] + z * waveZ[i] + layerPhase);
|
||||
}
|
||||
return 0.5D + 0.5D * Math.sin(sum * 1.4D + contourPhase);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
public final class ScalesNoise extends PatternNoise {
|
||||
private static final double TAU = Math.PI * 2D;
|
||||
private static final double RADIUS = 0.5D;
|
||||
private static final double ROW_HEIGHT = 0.5D;
|
||||
private static final double GROOVE_WIDTH = 0.055D;
|
||||
|
||||
private final long seed;
|
||||
private final double phaseX;
|
||||
private final double phaseZ;
|
||||
|
||||
public ScalesNoise(long seed) {
|
||||
this.seed = seed;
|
||||
phaseX = (mix(seed) >>> 11) * 0x1.0p-53D;
|
||||
phaseZ = (mix(seed + 0x9E3779B97F4A7C15L) >>> 11) * 0x1.0p-53D;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
x += phaseX + Math.sin(y * TAU * 0.5D) * 0.22D + y * 0.15D;
|
||||
z += phaseZ + Math.sin(y * TAU * 0.25D) * 0.12D + y * 0.2D;
|
||||
long row = (long) Math.floor(z / ROW_HEIGHT);
|
||||
double groove = 0D;
|
||||
double shade = 0D;
|
||||
for (int offsetZ = -1; offsetZ <= 1; offsetZ++) {
|
||||
long pointZ = row + offsetZ;
|
||||
double shift = (pointZ & 1L) * 0.5D;
|
||||
long column = (long) Math.floor(x - shift);
|
||||
double dz = z - pointZ * ROW_HEIGHT;
|
||||
for (int offsetX = -1; offsetX <= 1; offsetX++) {
|
||||
long pointX = column + offsetX;
|
||||
double dx = x - (pointX + shift);
|
||||
double radiusSquared = dx * dx + dz * dz;
|
||||
double distance;
|
||||
if (dz >= 0D) {
|
||||
distance = Math.abs(Math.sqrt(radiusSquared) - RADIUS);
|
||||
} else {
|
||||
double endpoint = Math.abs(dx) - RADIUS;
|
||||
distance = Math.sqrt(endpoint * endpoint + dz * dz);
|
||||
}
|
||||
double rim = Math.max(0D, 1D - distance / GROOVE_WIDTH);
|
||||
if (rim > 0D || radiusSquared < RADIUS * RADIUS && dz > 0D) {
|
||||
long hash = mix(seed ^ pointX * 0x9E3779B97F4A7C15L ^ pointZ * 0xC2B2AE3D27D4EB4FL);
|
||||
double variation = 0.75D + (hash >>> 11) * 0x1.0p-53D * 0.25D;
|
||||
groove = Math.max(groove, rim * rim * (3D - 2D * rim) * variation);
|
||||
double inside = Math.max(0D, 1D - radiusSquared / (RADIUS * RADIUS));
|
||||
double rise = Math.clamp(dz / 0.12D, 0D, 1D);
|
||||
shade = Math.max(shade, inside * rise * rise * (3D - 2D * rise) * variation);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0.72D + 0.22D * shade - 0.65D * groove;
|
||||
}
|
||||
|
||||
private static long mix(long value) {
|
||||
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
|
||||
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
|
||||
return value ^ (value >>> 31);
|
||||
}
|
||||
}
|
||||
+45
-74
@@ -3,96 +3,67 @@ package art.arcane.iris.util.project.noise;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
|
||||
public class SierpinskiTriangleNoise implements NoiseGenerator, OctaveNoise {
|
||||
private static final double TRI_SCALE = 0.18D;
|
||||
private static final double TRI_PROJECTION = 0.8660254037844386D;
|
||||
private static final double HEAT_SCALE = 0.33D;
|
||||
private final SimplexNoise heatSimplex;
|
||||
private int octaves;
|
||||
private static final double TRIANGLE_SIDE = 8D;
|
||||
private static final double INVERSE_HEIGHT = 2D / (Math.sqrt(3D) * TRIANGLE_SIDE);
|
||||
private static final double HEAT_SCALE = 6D;
|
||||
private static final int DEPTH = 4;
|
||||
private final SimplexNoise heat;
|
||||
|
||||
public SierpinskiTriangleNoise(long seed) {
|
||||
RNG rng = new RNG(seed);
|
||||
this.heatSimplex = new SimplexNoise(rng.nextParallelRNG(177L).lmax());
|
||||
this.octaves = 1;
|
||||
}
|
||||
|
||||
private double clip(double value) {
|
||||
if (value < 0D) {
|
||||
return 0D;
|
||||
}
|
||||
|
||||
if (value > 1D) {
|
||||
return 1D;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private double heat(double x, double z) {
|
||||
if (octaves <= 1) {
|
||||
return heatSimplex.noise(x * HEAT_SCALE, z * HEAT_SCALE);
|
||||
}
|
||||
|
||||
double frequency = 1D;
|
||||
double amplitude = 1D;
|
||||
double total = 0D;
|
||||
double max = 0D;
|
||||
|
||||
for (int i = 0; i < octaves; i++) {
|
||||
total += heatSimplex.noise((x * HEAT_SCALE) * frequency, (z * HEAT_SCALE) * frequency) * amplitude;
|
||||
max += amplitude;
|
||||
frequency *= 2D;
|
||||
amplitude *= 0.5D;
|
||||
}
|
||||
|
||||
if (max == 0D) {
|
||||
return 0.5D;
|
||||
}
|
||||
|
||||
return clip(total / max);
|
||||
}
|
||||
|
||||
private double mask(double x, double z) {
|
||||
double sx = (x * TRI_SCALE) + ((z * TRI_SCALE) * 0.5D);
|
||||
double sz = (z * TRI_SCALE) * TRI_PROJECTION;
|
||||
long ix = (long) Math.floor(Math.abs(sx));
|
||||
long iz = (long) Math.floor(Math.abs(sz));
|
||||
return ((ix & iz) == 0L) ? 1D : 0D;
|
||||
}
|
||||
|
||||
private double sample(double x, double z) {
|
||||
double m = mask(x, z);
|
||||
double h = heat(x, z);
|
||||
if (m >= 0.5D) {
|
||||
return clip((h * 0.65D) + 0.35D);
|
||||
}
|
||||
|
||||
return clip(h * 0.12D);
|
||||
this.heat = new SimplexNoise(new RNG(seed).nextParallelRNG(177L).lmax());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x) {
|
||||
return sample(x, 0D);
|
||||
return sample(x, 0D, 0D);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double z) {
|
||||
return sample(x, z);
|
||||
return sample(x, 0D, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double y, double z) {
|
||||
if (z == 0D) {
|
||||
return sample(x, y);
|
||||
}
|
||||
|
||||
double a = sample(x, z);
|
||||
double b = sample(y, x);
|
||||
double c = sample(z, y);
|
||||
return (a + b + c) / 3D;
|
||||
return sample(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOctaves(int o) {
|
||||
this.octaves = Math.max(1, o);
|
||||
public void setOctaves(int octaves) {
|
||||
heat.setOctaves(octaves);
|
||||
}
|
||||
|
||||
private boolean contains(double x, double z) {
|
||||
double v = z * INVERSE_HEIGHT;
|
||||
double u = (x / TRIANGLE_SIDE) - (v * 0.5D);
|
||||
u -= Math.floor(u);
|
||||
v -= Math.floor(v);
|
||||
|
||||
if (u + v > 1D) {
|
||||
u = 1D - u;
|
||||
v = 1D - v;
|
||||
}
|
||||
|
||||
for (int depth = 0; depth < DEPTH; depth++) {
|
||||
if (u >= 0.5D) {
|
||||
u = (u * 2D) - 1D;
|
||||
v *= 2D;
|
||||
} else if (v >= 0.5D) {
|
||||
u *= 2D;
|
||||
v = (v * 2D) - 1D;
|
||||
} else if (u + v <= 0.5D) {
|
||||
u *= 2D;
|
||||
v *= 2D;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private double sample(double x, double y, double z) {
|
||||
double value = heat.noise(x * HEAT_SCALE, y * HEAT_SCALE, z * HEAT_SCALE);
|
||||
return contains(x, z) ? (value * 0.65D) + 0.35D : value * 0.12D;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import art.arcane.volmlib.util.math.RNG;
|
||||
public class SimplexNoise implements NoiseGenerator, OctaveNoise {
|
||||
private final FastNoiseDouble n;
|
||||
private int octaves;
|
||||
private double octaveBounding = 1D;
|
||||
|
||||
public SimplexNoise(long seed) {
|
||||
this.n = new FastNoiseDouble(new RNG(seed).lmax());
|
||||
@@ -35,127 +36,74 @@ public class SimplexNoise implements NoiseGenerator, OctaveNoise {
|
||||
|
||||
@Override
|
||||
public double noise(double x) {
|
||||
if (octaves <= 1) {
|
||||
return f(n.GetSimplex(x, 0d));
|
||||
}
|
||||
|
||||
double f = 1;
|
||||
double m = 0;
|
||||
double v = 0;
|
||||
|
||||
for (int i = 0; i < octaves; i++) {
|
||||
v += n.GetSimplex((x * (f == 1 ? f++ : (f *= 2))), 0d) * f;
|
||||
m += f;
|
||||
}
|
||||
|
||||
return f(v / m);
|
||||
return f(noiseSigned(x));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noiseSigned(double x) {
|
||||
if (octaves <= 1) {
|
||||
if (octaves == 1) {
|
||||
return n.GetSimplex(x, 0D);
|
||||
}
|
||||
|
||||
double frequency = 1D;
|
||||
double magnitude = 0D;
|
||||
double amplitude = 1D;
|
||||
double value = 0D;
|
||||
|
||||
for (int i = 0; i < octaves; i++) {
|
||||
double sampleFrequency;
|
||||
if (frequency == 1D) {
|
||||
sampleFrequency = frequency;
|
||||
frequency = 2D;
|
||||
} else {
|
||||
frequency *= 2D;
|
||||
sampleFrequency = frequency;
|
||||
}
|
||||
|
||||
value += n.GetSimplex(x * sampleFrequency, 0D) * frequency;
|
||||
magnitude += frequency;
|
||||
value += n.GetSimplex(x * frequency, 0D) * amplitude;
|
||||
frequency *= 2D;
|
||||
amplitude *= 0.5D;
|
||||
}
|
||||
|
||||
return value / magnitude;
|
||||
return value * octaveBounding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double z) {
|
||||
if (octaves <= 1) {
|
||||
return f(n.GetSimplex(x, z));
|
||||
}
|
||||
double f = 1;
|
||||
double m = 0;
|
||||
double v = 0;
|
||||
|
||||
for (int i = 0; i < octaves; i++) {
|
||||
f = f == 1 ? f + 1 : f * 2;
|
||||
v += n.GetSimplex((x * f), (z * f)) * f;
|
||||
m += f;
|
||||
}
|
||||
|
||||
return f(v / m);
|
||||
return f(noiseSigned(x, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noiseSigned(double x, double z) {
|
||||
if (octaves <= 1) {
|
||||
if (octaves == 1) {
|
||||
return n.GetSimplex(x, z);
|
||||
}
|
||||
|
||||
double frequency = 1D;
|
||||
double magnitude = 0D;
|
||||
double amplitude = 1D;
|
||||
double value = 0D;
|
||||
|
||||
for (int i = 0; i < octaves; i++) {
|
||||
double octaveFrequency = frequency == 1D ? frequency + 1D : frequency * 2D;
|
||||
value += n.GetSimplex(x * octaveFrequency, z * octaveFrequency) * octaveFrequency;
|
||||
magnitude += octaveFrequency;
|
||||
frequency = octaveFrequency;
|
||||
value += n.GetSimplex(x * frequency, z * frequency) * amplitude;
|
||||
frequency *= 2D;
|
||||
amplitude *= 0.5D;
|
||||
}
|
||||
|
||||
return value / magnitude;
|
||||
return value * octaveBounding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double y, double z) {
|
||||
if (octaves <= 1) {
|
||||
return f(n.GetSimplex(x, y, z));
|
||||
}
|
||||
double f = 1;
|
||||
double m = 0;
|
||||
double v = 0;
|
||||
|
||||
for (int i = 0; i < octaves; i++) {
|
||||
f = f == 1 ? f + 1 : f * 2;
|
||||
v += n.GetSimplex((x * f), (y * f), (z * f)) * f;
|
||||
m += f;
|
||||
}
|
||||
|
||||
return f(v / m);
|
||||
return f(noiseSigned(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noiseSigned(double x, double y, double z) {
|
||||
if (octaves <= 1) {
|
||||
if (octaves == 1) {
|
||||
return n.GetSimplex(x, y, z);
|
||||
}
|
||||
|
||||
double frequency = 1D;
|
||||
double magnitude = 0D;
|
||||
double amplitude = 1D;
|
||||
double value = 0D;
|
||||
|
||||
for (int i = 0; i < octaves; i++) {
|
||||
double octaveFrequency = frequency == 1D ? frequency + 1D : frequency * 2D;
|
||||
value += n.GetSimplex(x * octaveFrequency, y * octaveFrequency, z * octaveFrequency) * octaveFrequency;
|
||||
magnitude += octaveFrequency;
|
||||
frequency = octaveFrequency;
|
||||
value += n.GetSimplex(x * frequency, y * frequency, z * frequency) * amplitude;
|
||||
frequency *= 2D;
|
||||
amplitude *= 0.5D;
|
||||
}
|
||||
|
||||
return value / magnitude;
|
||||
return value * octaveBounding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOctaves(int o) {
|
||||
octaves = o;
|
||||
public void setOctaves(int octaves) {
|
||||
this.octaves = Math.max(1, Math.min(16, octaves));
|
||||
octaveBounding = 1D / (2D - Math.scalb(1D, 1 - this.octaves));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import java.util.SplittableRandom;
|
||||
|
||||
public final class StrataNoise extends PatternNoise {
|
||||
private static final double TAU = Math.PI * 2D;
|
||||
|
||||
private final double cosine;
|
||||
private final double sine;
|
||||
private final double phaseA;
|
||||
private final double phaseB;
|
||||
private final double phaseC;
|
||||
|
||||
public StrataNoise(long seed) {
|
||||
SplittableRandom random = new SplittableRandom(seed);
|
||||
double angle = random.nextDouble(TAU);
|
||||
cosine = Math.cos(angle);
|
||||
sine = Math.sin(angle);
|
||||
phaseA = random.nextDouble(TAU);
|
||||
phaseB = random.nextDouble(TAU);
|
||||
phaseC = random.nextDouble(TAU);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
double along = x * cosine + z * sine;
|
||||
double across = z * cosine - x * sine;
|
||||
double fold = 1.1D * Math.sin(along * 1.1D + phaseA)
|
||||
+ 0.35D * Math.sin(along * 2.7D + across * 0.8D + phaseB + y * 0.4D)
|
||||
+ 0.2D * Math.sin(across * 1.7D + y * 0.9D + phaseC);
|
||||
double layer = TAU * (across + y * 0.75D + fold);
|
||||
double thickness = layer + 0.55D * Math.sin(layer * 0.25D + phaseB);
|
||||
double broadBand = 0.5D + 0.5D * Math.sin(thickness);
|
||||
double lamina = 0.5D + 0.5D * Math.sin(thickness * 3D + phaseC);
|
||||
return 0.82D * broadBand + 0.18D * lamina;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
public final class TruchetNoise extends PatternNoise {
|
||||
private static final double RIBBON_WIDTH = 0.075D;
|
||||
private static final double HEIGHT_FREQUENCY = Math.PI * 0.25D;
|
||||
|
||||
private final long seed;
|
||||
private final double phaseX;
|
||||
private final double phaseZ;
|
||||
|
||||
public TruchetNoise(long seed) {
|
||||
this.seed = seed;
|
||||
phaseX = 0.17D + (mix(seed) >>> 11) * 0x1.0p-53D * 0.61D;
|
||||
phaseZ = 0.13D + (mix(seed + 0x9E3779B97F4A7C15L) >>> 11) * 0x1.0p-53D * 0.73D;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
x += phaseX;
|
||||
z += phaseZ;
|
||||
if (y != 0D) {
|
||||
double angle = y * HEIGHT_FREQUENCY;
|
||||
x += Math.sin(angle) * 0.3D + y * 0.125D;
|
||||
z += (Math.cos(angle) - 1D) * 0.3D;
|
||||
}
|
||||
|
||||
long cellX = (long) Math.floor(x);
|
||||
long cellZ = (long) Math.floor(z);
|
||||
double localX = x - cellX;
|
||||
double localZ = z - cellZ;
|
||||
long orientation = mix(seed ^ cellX * 0x9E3779B97F4A7C15L ^ cellZ * 0xC2B2AE3D27D4EB4FL);
|
||||
if ((orientation & 1L) != 0L) {
|
||||
localX = 1D - localX;
|
||||
}
|
||||
|
||||
double oppositeX = 1D - localX;
|
||||
double oppositeZ = 1D - localZ;
|
||||
double first = Math.abs(Math.sqrt(localX * localX + localZ * localZ) - 0.5D);
|
||||
double second = Math.abs(Math.sqrt(oppositeX * oppositeX + oppositeZ * oppositeZ) - 0.5D);
|
||||
double ribbon = Math.max(0D, 1D - Math.min(first, second) / RIBBON_WIDTH);
|
||||
return ribbon * ribbon * (3D - 2D * ribbon);
|
||||
}
|
||||
|
||||
private static long mix(long value) {
|
||||
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
|
||||
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
|
||||
return value ^ (value >>> 31);
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ public class VascularNoise implements NoiseGenerator {
|
||||
}
|
||||
|
||||
private double filter(double noise) {
|
||||
double normalized = (noise * 0.5D) + 0.5D;
|
||||
double normalized = 0.5D - noise * 0.5D;
|
||||
if (normalized < 0D) {
|
||||
return 0D;
|
||||
}
|
||||
@@ -40,6 +40,7 @@ public class VascularNoise implements NoiseGenerator {
|
||||
}
|
||||
|
||||
private double filterSigned(double noise) {
|
||||
noise = -noise;
|
||||
if (noise < -1D) {
|
||||
return -1D;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
public final class VortexNoise extends PatternNoise {
|
||||
private static final double RADIUS = 0.9D;
|
||||
private static final double RADIUS_SQUARED = RADIUS * RADIUS;
|
||||
private static final double TAU = Math.PI * 2D;
|
||||
|
||||
private final long seed;
|
||||
|
||||
public VortexNoise(long seed) {
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
long cellX = (long) Math.floor(x);
|
||||
long cellZ = (long) Math.floor(z);
|
||||
double sum = 0D;
|
||||
double weightSum = 0.07D;
|
||||
for (int offsetX = -1; offsetX <= 1; offsetX++) {
|
||||
long gridX = cellX + offsetX;
|
||||
for (int offsetZ = -1; offsetZ <= 1; offsetZ++) {
|
||||
long gridZ = cellZ + offsetZ;
|
||||
long hash = hash(gridX, gridZ);
|
||||
double dx = x - (gridX + 0.2D + unit(hash) * 0.6D);
|
||||
hash = mix(hash);
|
||||
double dz = z - (gridZ + 0.2D + unit(hash) * 0.6D);
|
||||
double radiusSquared = dx * dx + dz * dz;
|
||||
if (radiusSquared >= RADIUS_SQUARED) {
|
||||
continue;
|
||||
}
|
||||
double radius = Math.sqrt(radiusSquared);
|
||||
double envelope = 1D - radiusSquared / RADIUS_SQUARED;
|
||||
double weight = envelope * envelope;
|
||||
hash = mix(hash);
|
||||
int arms = 2 + (int) (hash & 1L);
|
||||
int direction = (hash & 2L) == 0L ? -1 : 1;
|
||||
double phase = TAU * unit(hash);
|
||||
hash = mix(hash);
|
||||
double winding = 12D + unit(hash) * 4D;
|
||||
double wave = Math.sin(direction * arms * Math.atan2(dz, dx)
|
||||
- winding * radius + phase + y * TAU * 0.5D);
|
||||
sum += weight * radius / (radius + 0.06D) * wave;
|
||||
weightSum += weight;
|
||||
}
|
||||
}
|
||||
return 0.5D + 0.5D * sum / weightSum;
|
||||
}
|
||||
|
||||
private long hash(long x, long z) {
|
||||
return mix(seed ^ x * 0x9E3779B97F4A7C15L ^ z * 0xD1B54A32D192ED03L);
|
||||
}
|
||||
|
||||
private static long mix(long value) {
|
||||
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
|
||||
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
|
||||
return value ^ (value >>> 31);
|
||||
}
|
||||
|
||||
private static double unit(long value) {
|
||||
return (value >>> 11) * 0x1.0p-53;
|
||||
}
|
||||
}
|
||||
@@ -18,13 +18,11 @@
|
||||
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
|
||||
public class WhiteNoise implements NoiseGenerator {
|
||||
private final FastNoise n;
|
||||
private final FastNoiseDouble n;
|
||||
|
||||
public WhiteNoise(long seed) {
|
||||
n = new FastNoise(new RNG(seed).imax());
|
||||
n = new FastNoiseDouble(seed);
|
||||
}
|
||||
|
||||
public boolean isStatic() {
|
||||
@@ -35,22 +33,18 @@ public class WhiteNoise implements NoiseGenerator {
|
||||
return true;
|
||||
}
|
||||
|
||||
private double f(double m) {
|
||||
return (m % 8192) * 1024;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x) {
|
||||
return (n.GetWhiteNoise(f(x), 0d) / 2D) + 0.5D;
|
||||
return (n.GetWhiteNoise(x, 0d) / 2D) + 0.5D;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double z) {
|
||||
return (n.GetWhiteNoise(f(x), f(z)) / 2D) + 0.5D;
|
||||
return (n.GetWhiteNoise(x, z) / 2D) + 0.5D;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double y, double z) {
|
||||
return (n.GetWhiteNoise(f(x), f(y), f(z)) / 2D) + 0.5D;
|
||||
return (n.GetWhiteNoise(x, y, z) / 2D) + 0.5D;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
public final class WoodNoise extends PatternNoise {
|
||||
private static final double TAU = Math.PI * 2D;
|
||||
|
||||
private final long seed;
|
||||
private final double cosine;
|
||||
private final double sine;
|
||||
private final double phase;
|
||||
|
||||
public WoodNoise(long seed) {
|
||||
this.seed = seed;
|
||||
phase = (mix(seed) >>> 11) * 0x1.0p-53D * TAU;
|
||||
cosine = Math.cos(phase);
|
||||
sine = Math.sin(phase);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double sample(double x, double y, double z) {
|
||||
double along = (z * cosine - x * sine) * 0.45D + y * 0.17D;
|
||||
double across = x * cosine + z * sine + y * 0.07D + 0.11D * Math.sin(along * 0.85D + phase);
|
||||
double grainPhase = across * TAU * 3D + 0.18D * Math.sin(along * 2.1D + phase);
|
||||
double value = 0.12D * (0.5D + 0.5D * Math.sin(grainPhase));
|
||||
double weightSum = 0.12D;
|
||||
long cellAcross = (long) Math.floor(across);
|
||||
long cellAlong = (long) Math.floor(along);
|
||||
for (int offsetAcross = -1; offsetAcross <= 1; offsetAcross++) {
|
||||
long gridAcross = cellAcross + offsetAcross;
|
||||
for (int offsetAlong = -1; offsetAlong <= 1; offsetAlong++) {
|
||||
long gridAlong = cellAlong + offsetAlong;
|
||||
long hash = mix(seed ^ gridAcross * 0x9E3779B97F4A7C15L ^ gridAlong * 0xD1B54A32D192ED03L);
|
||||
double deltaAcross = across - (gridAcross + 0.3D + (hash & 65535L) * (0.4D / 65535D));
|
||||
double deltaAlong = along - (gridAlong + 0.3D + ((hash >>> 16) & 65535L) * (0.4D / 65535D));
|
||||
double radius = 0.5D + ((hash >>> 32) & 255L) * (0.12D / 255D);
|
||||
double distanceSquared = deltaAcross * deltaAcross + deltaAlong * deltaAlong;
|
||||
double envelope = 1D - distanceSquared / (radius * radius);
|
||||
if (envelope <= 0D) {
|
||||
continue;
|
||||
}
|
||||
double rings = Math.sqrt(distanceSquared + 0.0004D) * TAU * 5D;
|
||||
double ringPhase = ((hash >>> 40) & 65535L) * (TAU / 65535D);
|
||||
double knot = 0.5D + 0.5D * Math.sin(rings + ringPhase);
|
||||
double weight = 3D * envelope * envelope;
|
||||
value += knot * weight;
|
||||
weightSum += weight;
|
||||
}
|
||||
}
|
||||
return value / weightSum;
|
||||
}
|
||||
|
||||
private static long mix(long value) {
|
||||
value = (value ^ (value >>> 30)) * 0xBF58476D1CE4E5B9L;
|
||||
value = (value ^ (value >>> 27)) * 0x94D049BB133111EBL;
|
||||
return value ^ (value >>> 31);
|
||||
}
|
||||
}
|
||||
+12
-10
@@ -51,6 +51,7 @@ import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class IrisCaveCarver3DNearParityTest {
|
||||
private static final long CARVE_SEED = -54_777_863_784_492_918L;
|
||||
private static final int SURFACE_CEILING_FADE_DEPTH = 12;
|
||||
private static final double SURFACE_CEILING_SOLID_EPSILON = 0.000001D;
|
||||
|
||||
@@ -297,11 +298,11 @@ public class IrisCaveCarver3DNearParityTest {
|
||||
public void warpCacheDoesNotCrossContaminateCarverInstancesOnSameThread() throws Exception {
|
||||
Engine engine = createEngine(128, 92);
|
||||
IrisCaveProfile firstProfile = createProfile(true, false);
|
||||
firstProfile.setWarpStyle(new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.12D));
|
||||
firstProfile.setWarpStyle(new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.1875D));
|
||||
IrisCaveProfile secondProfile = createProfile(true, false);
|
||||
secondProfile.setWarpStyle(new IrisGeneratorStyle(NoiseStyle.CELLULAR).zoomed(0.12D));
|
||||
secondProfile.setWarpStyle(new IrisGeneratorStyle(NoiseStyle.CELLULAR).zoomed(0.1875D));
|
||||
IrisCaveProfile controlProfile = createProfile(true, false);
|
||||
controlProfile.setWarpStyle(new IrisGeneratorStyle(NoiseStyle.CELLULAR).zoomed(0.12D));
|
||||
controlProfile.setWarpStyle(new IrisGeneratorStyle(NoiseStyle.CELLULAR).zoomed(0.1875D));
|
||||
IrisCaveCarver3D firstCarver = new IrisCaveCarver3D(engine, firstProfile);
|
||||
IrisCaveCarver3D secondCarver = new IrisCaveCarver3D(engine, secondProfile);
|
||||
IrisCaveCarver3D controlCarver = new IrisCaveCarver3D(engine, controlProfile);
|
||||
@@ -1101,13 +1102,14 @@ public class IrisCaveCarver3DNearParityTest {
|
||||
Engine engine = mock(Engine.class);
|
||||
IrisData data = mock(IrisData.class);
|
||||
IrisDimension dimension = mock(IrisDimension.class);
|
||||
SeedManager seedManager = new SeedManager(942_337_445L);
|
||||
SeedManager seedManager = mock(SeedManager.class);
|
||||
EngineMetrics metrics = new EngineMetrics(16);
|
||||
IrisWorld world = IrisWorld.builder().minHeight(0).maxHeight(worldHeight).build();
|
||||
|
||||
doReturn(data).when(engine).getData();
|
||||
doReturn(dimension).when(engine).getDimension();
|
||||
doReturn(seedManager).when(engine).getSeedManager();
|
||||
doReturn(CARVE_SEED).when(seedManager).getCarve();
|
||||
doReturn(metrics).when(engine).getMetrics();
|
||||
doReturn(world).when(engine).getWorld();
|
||||
doReturn(sampledHeight).when(engine).getHeight(anyInt(), anyInt());
|
||||
@@ -1124,10 +1126,10 @@ public class IrisCaveCarver3DNearParityTest {
|
||||
profile.setVerticalRange(new IrisRange(0D, 120D));
|
||||
profile.setVerticalEdgeFade(14);
|
||||
profile.setVerticalEdgeFadeStrength(0.21D);
|
||||
profile.setBaseDensityStyle(new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.07D));
|
||||
profile.setDetailDensityStyle(new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.17D));
|
||||
profile.setWarpStyle(new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.12D));
|
||||
profile.setSurfaceBreakStyle(new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.09D));
|
||||
profile.setBaseDensityStyle(new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.109375D));
|
||||
profile.setDetailDensityStyle(new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.265625D));
|
||||
profile.setWarpStyle(new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.1875D));
|
||||
profile.setSurfaceBreakStyle(new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.140625D));
|
||||
profile.setBaseWeight(1D);
|
||||
profile.setDetailWeight(0.48D);
|
||||
profile.setWarpStrength(warp ? 0.37D : 0D);
|
||||
@@ -1146,14 +1148,14 @@ public class IrisCaveCarver3DNearParityTest {
|
||||
if (modules) {
|
||||
KList<IrisCaveFieldModule> caveModules = new KList<>();
|
||||
caveModules.add(new IrisCaveFieldModule(
|
||||
new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.11D),
|
||||
new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.171875D),
|
||||
0.23D,
|
||||
0.04D,
|
||||
new IrisRange(0D, 72D),
|
||||
false
|
||||
));
|
||||
caveModules.add(new IrisCaveFieldModule(
|
||||
new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.19D),
|
||||
new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.296875D),
|
||||
0.17D,
|
||||
-0.06D,
|
||||
new IrisRange(24D, 120D),
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ package art.arcane.iris.engine.mantle.components;
|
||||
|
||||
import art.arcane.iris.engine.object.IrisStructureCarveShape;
|
||||
import art.arcane.iris.util.project.noise.CNG;
|
||||
import art.arcane.iris.util.project.noise.NoiseType;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -99,7 +100,7 @@ public class StructureCarveEnvelopeTest {
|
||||
|
||||
@Test
|
||||
public void lobedSideReachWandersInsideTheConfiguredPaddingBand() {
|
||||
CNG lobe = new CNG(new RNG(1337L), 1D, 1);
|
||||
CNG lobe = new CNG(new RNG(1337L), NoiseType.CLOVER, 1D, 1);
|
||||
double lowest = 14D;
|
||||
double highest = 0D;
|
||||
|
||||
|
||||
+7
-7
@@ -17,10 +17,10 @@ public class IrisMathNoiseHotPathParityTest {
|
||||
HexRandomSizeNoise randomSize = new HexRandomSizeNoise(12345L);
|
||||
HexJamesNoise james = new HexJamesNoise(54321L);
|
||||
|
||||
assertEquals(0.4670864519829246D, randomSize.noise(12.5D, -8.25D), 0D);
|
||||
assertEquals(0.4935590260372404D, randomSize.noise(-31.75D, 44.5D, 9.125D), 0D);
|
||||
assertEquals(0.5185375921658786D, james.noise(12.5D, -8.25D), 0D);
|
||||
assertEquals(0.4557738681128938D, james.noise(-31.75D, 44.5D, 9.125D), 0D);
|
||||
assertEquals(0.4924242426867459D, randomSize.noise(12.5D, -8.25D), 0D);
|
||||
assertEquals(0.4856492100555178D, randomSize.noise(-31.75D, 44.5D, 9.125D), 0D);
|
||||
assertEquals(0.3769400526313378D, james.noise(12.5D, -8.25D), 0D);
|
||||
assertEquals(0.5487488096537423D, james.noise(-31.75D, 44.5D, 9.125D), 0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -33,17 +33,17 @@ public class IrisMathNoiseHotPathParityTest {
|
||||
.setStyle(NoiseStyle.PERLIN_IRIS.style())
|
||||
.setExponent(1.15D);
|
||||
|
||||
assertEquals(0.3886291844359823D, noiseGenerator.getNoise(9988L, 31.25D, -17.5D, null), 0D);
|
||||
assertEquals(0.5084984235498856D, noiseGenerator.getNoise(9988L, 31.25D, -17.5D, null), 0D);
|
||||
|
||||
IrisGeneratorStyle style = NoiseStyle.IRIS_DOUBLE.style();
|
||||
assertEquals(0.8640377573263068D, style.createNoCache(new RNG(112233L), null).noiseFast2D(42.5D, -19.75D), 0D);
|
||||
assertEquals(0.6224926166486215D, style.createNoCache(new RNG(112233L), null).noiseFast2D(42.5D, -19.75D), 0D);
|
||||
|
||||
IrisGenerator generator = new IrisGenerator()
|
||||
.setZoom(9.5D)
|
||||
.setOpacity(0.91D)
|
||||
.setComposite(new KList<IrisNoiseGenerator>().qadd(noiseGenerator));
|
||||
|
||||
assertEquals(0.5671980255964687D, generator.getHeight(63.0D, -27.0D, 445566L), 0D);
|
||||
assertEquals(0.43997118860461754D, generator.getHeight(63.0D, -27.0D, 445566L), 0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -16,6 +16,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -78,13 +79,11 @@ public final class IrisNoiseGeneratorCacheTest {
|
||||
|
||||
private GeneratorFixture() {
|
||||
when(data.getEngine()).thenReturn(engine);
|
||||
when(firstGenerator.oct(OCTAVES)).thenReturn(firstGenerator);
|
||||
when(secondGenerator.oct(OCTAVES)).thenReturn(secondGenerator);
|
||||
Map<Long, CNG> generators = Map.of(
|
||||
11L + GENERATOR_SALT - CONFIGURED_SEED, firstGenerator,
|
||||
29L + GENERATOR_SALT - CONFIGURED_SEED, secondGenerator
|
||||
);
|
||||
when(style.createNoCache(any(RNG.class), same(data))).thenAnswer(invocation -> {
|
||||
when(style.createForLayer(any(RNG.class), same(data), eq(OCTAVES))).thenAnswer(invocation -> {
|
||||
RNG rng = invocation.getArgument(0);
|
||||
return generators.get(rng.getSeed());
|
||||
});
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class IrisNoiseGeneratorRemapTest {
|
||||
@Test
|
||||
public void parametricCurvePreservesEndpointsAndShapesBothHalves() {
|
||||
double[][] samples = {{0D, 0D}, {0.25D, 0.1D}, {0.5D, 0.5D}, {0.75D, 0.9D}, {1D, 1D}};
|
||||
for (double[] sample : samples) {
|
||||
IrisNoiseGenerator layer = new IrisNoiseGenerator()
|
||||
.setStyle(NoiseStyle.FLAT.style())
|
||||
.setOpacity(sample[0])
|
||||
.setParametric(true);
|
||||
assertEquals(sample[1], layer.getNoise(75L, 13D, -21D, null), 0.000000000001D);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.util.project.noise.CNG;
|
||||
import art.arcane.iris.util.project.noise.NoiseGenerator;
|
||||
import art.arcane.iris.util.project.noise.NoiseType;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public final class NoiseStyleContractTest {
|
||||
@Rule
|
||||
public TemporaryFolder folder = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void builtInStylesAreFiniteAndRepeatableAcrossWorldCoordinates() {
|
||||
double[] coordinates = {-29_999_983.25D, -513D, -64D, -1D, 0D, 0.125D, 17D, 64D, 8192D, 29_999_983.25D};
|
||||
for (NoiseStyle style : NoiseStyle.values()) {
|
||||
CNG first = style.create(new RNG(813753L));
|
||||
CNG second = style.create(new RNG(813753L));
|
||||
for (double coordinate : coordinates) {
|
||||
double x = coordinate;
|
||||
double z = coordinate * -0.713D;
|
||||
double[] samples = {first.noise(x), first.noise(x, z), first.noise(x, 81.25D, z)};
|
||||
for (double sample : samples) {
|
||||
assertTrue(style + " is non-finite at " + coordinate, Double.isFinite(sample));
|
||||
}
|
||||
assertEquals(style.name(), samples[0], second.noise(x), 0D);
|
||||
assertEquals(style.name(), samples[1], second.noise(x, z), 0D);
|
||||
assertEquals(style.name(), samples[2], second.noise(x, 81.25D, z), 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void classicPresetsShareOneBaseScale() {
|
||||
NoiseStyle[] styles = {NoiseStyle.SIMPLEX, NoiseStyle.PERLIN, NoiseStyle.CUBIC,
|
||||
NoiseStyle.FRACTAL_CUBIC, NoiseStyle.BIOCTAVE_SIMPLEX, NoiseStyle.VIGOCTAVE_SIMPLEX,
|
||||
NoiseStyle.BIOCTAVE_FRACTAL_CUBIC, NoiseStyle.QUADOCTAVE_FRACTAL_CUBIC};
|
||||
for (NoiseStyle style : styles) {
|
||||
CNG generator = style.create(new RNG(7751L));
|
||||
NoiseGenerator source = generator.getGenerator();
|
||||
double scale = 100D / 64D;
|
||||
assertEquals(style.name(), source.noise(27.5D * scale, -83.25D * scale),
|
||||
generator.noise(27.5D, -83.25D), 0D);
|
||||
}
|
||||
assertEquals(2D / 64D, NoiseType.HEXAGON.getCoordinateScale(), 0D);
|
||||
assertEquals(8D / 64D, NoiseType.SIERPINSKI_TRIANGLE.getCoordinateScale(), 0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void explicitNativeGeneratorFrequenciesKeepTheirUnits() {
|
||||
CNG generator = new CNG(new RNG(7231L), NoiseType.SIMPLEX, 1D, 1).scale(0.375D);
|
||||
assertEquals(generator.getGenerator().noise(28D * 0.375D, -74D * 0.375D),
|
||||
generator.noise(28D, -74D), 0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void explicitZoomDoublesFeatureSize() {
|
||||
for (NoiseStyle style : NoiseStyle.values()) {
|
||||
if (style == NoiseStyle.STATIC) {
|
||||
continue;
|
||||
}
|
||||
CNG normal = style.style().createNoCache(new RNG(45231L), null);
|
||||
CNG enlarged = style.style().zoomed(2D).createNoCache(new RNG(45231L), null);
|
||||
assertEquals(style.name(), normal.noise(13.25D, -17.75D),
|
||||
enlarged.noise(26.5D, -35.5D), 0.000000001D);
|
||||
assertEquals(style.name(), normal.noise(13.25D), enlarged.noise(26.5D), 0.000000001D);
|
||||
assertEquals(style.name(), normal.noise(13.25D, 41.125D, -17.75D),
|
||||
enlarged.noise(26.5D, 82.25D, -35.5D), 0.000000001D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultSettingsPreserveEveryPreset() {
|
||||
for (NoiseStyle style : NoiseStyle.values()) {
|
||||
CNG direct = style.create(new RNG(7253L));
|
||||
CNG configured = style.style().createNoCache(new RNG(7253L), null);
|
||||
for (int index = 0; index < 32; index++) {
|
||||
double x = index * 19.713D - 400D;
|
||||
double z = index * -31.579D + 100D;
|
||||
assertEquals(style.name(), direct.noise(x, z), configured.noise(x, z), 0D);
|
||||
assertEquals(style.name(), direct.noise(x, 32.75D, z), configured.noise(x, 32.75D, z), 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builtInStylesStayInsideTheirUnitRange() {
|
||||
for (NoiseStyle style : NoiseStyle.values()) {
|
||||
for (long seed : new long[]{0L, 813753L, Long.MAX_VALUE}) {
|
||||
CNG generator = style.create(new RNG(seed));
|
||||
for (int index = 0; index < 256; index++) {
|
||||
double x = index * 17.12345D - 2000D;
|
||||
double z = index * -27.98765D + 3000D;
|
||||
double[] values = {generator.noise(x), generator.noise(x, z), generator.noise(x, 53.75D, z)};
|
||||
for (double value : values) {
|
||||
assertTrue(style + " produced " + value, value >= 0D && value <= 1D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void explicitFractureAndCellularizationZoomWithTheirSource() {
|
||||
IrisGeneratorStyle[] styles = {
|
||||
NoiseStyle.SIMPLEX.style().setFracture(NoiseStyle.CLOVER.style().setMultiplier(50D).setZoom(1.5D)),
|
||||
NoiseStyle.IRIS.style().setCellularFrequency(0.7D).setCellularZoom(1.25D)
|
||||
};
|
||||
for (IrisGeneratorStyle style : styles) {
|
||||
CNG normal = style.createNoCache(new RNG(217L), null);
|
||||
CNG enlarged = style.setZoom(2D).createNoCache(new RNG(217L), null);
|
||||
assertEquals(normal.noise(19.25D, -71.75D), enlarged.noise(38.5D, -143.5D), 0.000000001D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuredExponentCompoundsThePresetCurve() {
|
||||
CNG base = NoiseStyle.VASCULAR_THIN.style().createNoCache(new RNG(8831L), null);
|
||||
CNG squared = NoiseStyle.VASCULAR_THIN.style().setExponent(2D).createNoCache(new RNG(8831L), null);
|
||||
double value = base.noise(29.5D, -33.25D);
|
||||
assertEquals(value * value, squared.noise(29.5D, -33.25D), 0.000000000001D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void octavePresetsKeepTheirBaseSeed() {
|
||||
NoiseStyle[][] families = {
|
||||
{NoiseStyle.SIMPLEX, NoiseStyle.BIOCTAVE_SIMPLEX},
|
||||
{NoiseStyle.FRACTAL_FBM_SIMPLEX, NoiseStyle.BIOCTAVE_FRACTAL_FBM_SIMPLEX},
|
||||
{NoiseStyle.FRACTAL_BILLOW_SIMPLEX, NoiseStyle.BIOCTAVE_FRACTAL_BILLOW_SIMPLEX},
|
||||
{NoiseStyle.FRACTAL_RM_SIMPLEX, NoiseStyle.BIOCTAVE_FRACTAL_RM_SIMPLEX},
|
||||
{NoiseStyle.FRACTAL_BILLOW_PERLIN, NoiseStyle.BIOCTAVE_FRACTAL_BILLOW_PERLIN},
|
||||
{NoiseStyle.FRACTAL_CUBIC, NoiseStyle.BIOCTAVE_FRACTAL_CUBIC}
|
||||
};
|
||||
for (NoiseStyle[] family : families) {
|
||||
CNG configured = family[0].create(new RNG(82731L)).oct(2);
|
||||
CNG preset = family[1].create(new RNG(82731L));
|
||||
for (int index = 0; index < 32; index++) {
|
||||
double x = index * 7.125D;
|
||||
double z = index * -11.875D;
|
||||
assertEquals(family[1].name(), configured.noise(x, z), preset.noise(x, z), 0D);
|
||||
assertEquals(family[1].name(), configured.noise(x, 31.25D, z), preset.noise(x, 31.25D, z), 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void layerOctavesPreserveAndMultiplyPresetDetail() {
|
||||
IrisGeneratorStyle style = NoiseStyle.QUADOCTAVE_SIMPLEX.style();
|
||||
CNG unchanged = style.createForLayer(new RNG(71L), null, 1);
|
||||
CNG detailed = style.createForLayer(new RNG(71L), null, 2);
|
||||
assertEquals(4, unchanged.getOct());
|
||||
assertEquals(8, detailed.getOct());
|
||||
assertEquals(16, style.createForLayer(new RNG(71L), null, Integer.MAX_VALUE).getOct());
|
||||
assertNotEquals(unchanged.noise(21D, -31D), detailed.noise(21D, -31D), 0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cachedLayersRetainTheirOctavesAcrossReload() throws Exception {
|
||||
File dataFolder = folder.newFolder("pack");
|
||||
IrisData data = mock(IrisData.class);
|
||||
when(data.getDataFolder()).thenReturn(dataFolder);
|
||||
IrisGeneratorStyle cached = NoiseStyle.BIOCTAVE_SIMPLEX.style().setCacheSize(32);
|
||||
CNG first = cached.createForLayer(new RNG(921L), data, 3);
|
||||
CNG reloaded = cached.createForLayer(new RNG(921L), data, 3);
|
||||
CNG uncached = NoiseStyle.BIOCTAVE_SIMPLEX.style().createForLayer(new RNG(921L), data, 3);
|
||||
CNG different = cached.createForLayer(new RNG(921L), data, 1);
|
||||
assertEquals(6, first.getOct());
|
||||
assertEquals(6, reloaded.getOct());
|
||||
assertEquals(uncached.noise(4D, 7D), first.noise(4D, 7D), 0.000001D);
|
||||
assertEquals(first.noise(4D, 7D), reloaded.noise(4D, 7D), 0D);
|
||||
assertNotEquals(first.noise(4D, 7D), different.noise(4D, 7D), 0D);
|
||||
File[] caches = new File(dataFolder, ".cache").listFiles((parent, name) -> name.endsWith(".cnm"));
|
||||
assertEquals(2, caches.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidTransformsFailBeforeSampling() {
|
||||
double[] invalid = {0D, -1D, Double.NaN, Double.POSITIVE_INFINITY, Double.MIN_VALUE};
|
||||
for (double value : invalid) {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> NoiseStyle.SIMPLEX.style().setZoom(value).createNoCache(new RNG(1L), null));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> NoiseStyle.SIMPLEX.style().setExponent(value).createNoCache(new RNG(1L), null));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> NoiseStyle.SIMPLEX.style().setCellularFrequency(1D).setCellularZoom(value)
|
||||
.createNoCache(new RNG(1L), null));
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
-1
@@ -37,6 +37,26 @@ public class IrisInterpolationBoundsTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fractionalBoundsMatchSeparatePassesAndSampleEachCoordinateOnce() {
|
||||
for (InterpolationMethod method : InterpolationMethod.values()) {
|
||||
Map<Coordinate, NoiseBounds> expectedSamples = new HashMap<>();
|
||||
NoiseBounds expected = separatePasses(method, -31.75D, 29.25D, 32D,
|
||||
IrisInterpolationBoundsTest::asymmetricBounds, expectedSamples);
|
||||
Map<Coordinate, Integer> actualSamples = new HashMap<>();
|
||||
NoiseBounds actual = IrisInterpolation.getNoiseBounds(method, -31.75D, 29.25D, 32D, (x, z) -> {
|
||||
actualSamples.merge(new Coordinate(x, z), 1, Integer::sum);
|
||||
return asymmetricBounds(x, z);
|
||||
});
|
||||
|
||||
assertBounds(expected, actual);
|
||||
assertEquals(expectedSamples.keySet(), actualSamples.keySet());
|
||||
for (int calls : actualSamples.values()) {
|
||||
assertEquals(1, calls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedProvidersKeepTheirOwnSamplingSequences() {
|
||||
for (InterpolationMethod method : InterpolationMethod.values()) {
|
||||
@@ -95,7 +115,7 @@ public class IrisInterpolationBoundsTest {
|
||||
assertBounds(expected, actual);
|
||||
}
|
||||
|
||||
private static NoiseBounds separatePasses(InterpolationMethod method, int x, int z, double scale,
|
||||
private static NoiseBounds separatePasses(InterpolationMethod method, double x, double z, double scale,
|
||||
NoiseBoundsProvider provider, Map<Coordinate, NoiseBounds> samples) {
|
||||
double minimum = IrisInterpolation.getNoise(method, x, z, scale,
|
||||
(sx, sz) -> samples.computeIfAbsent(new Coordinate(sx, sz),
|
||||
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
package art.arcane.iris.util.project.interpolation;
|
||||
|
||||
import art.arcane.volmlib.util.function.NoiseProvider;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class IrisInterpolationSamplingTest {
|
||||
@Test
|
||||
public void bicubicDispatchUsesSixteenSamplesAndTheCubicKernel() {
|
||||
NoiseProvider source = (x, z) -> x * x + z * z * z;
|
||||
AtomicInteger calls = new AtomicInteger();
|
||||
NoiseProvider countingSource = (x, z) -> {
|
||||
calls.incrementAndGet();
|
||||
return source.noise(x, z);
|
||||
};
|
||||
double expected = IrisInterpolation.getBicubicNoise(3, 5, 8D, source);
|
||||
double bilinear = IrisInterpolation.getBilinearNoise(3, 5, 8D, source);
|
||||
double actual = IrisInterpolation.getNoise(InterpolationMethod.BICUBIC, 3, 5, 8D, countingSource);
|
||||
|
||||
assertEquals(expected, actual, 0D);
|
||||
assertNotEquals(bilinear, actual, 1.0E-12D);
|
||||
assertEquals(16, calls.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bilinearSamplingPreservesFractionalAndNegativeCoordinates() {
|
||||
NoiseProvider source = (x, z) -> x * 0.001D + z * 0.002D;
|
||||
|
||||
for (double x : new double[]{-31.75D, -0.25D, 0D, 0.75D, 31.25D, 29_999_999.25D}) {
|
||||
for (double z : new double[]{-33.5D, -0.5D, 0.5D, 23.75D}) {
|
||||
assertEquals(source.noise(x, z),
|
||||
IrisInterpolation.getNoise(InterpolationMethod.BILINEAR, x, z, 32D, source), 1.0E-11D);
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(-1, IrisInterpolation.getRadiusFactor(-0.25D, 2.5D));
|
||||
assertEquals(0, IrisInterpolation.getRadiusFactor(2.25D, 2.5D));
|
||||
assertEquals(1, IrisInterpolation.getRadiusFactor(2.75D, 2.5D));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void starcastPreservesFractionalCentersNearTheWorldBorder() {
|
||||
double x = 30_000_000.25D;
|
||||
double z = -29_999_999.75D;
|
||||
List<Coordinate> samples = new ArrayList<>();
|
||||
IrisInterpolation.getNoise(InterpolationMethod.STARCAST_3, x, z, 8D, (sampleX, sampleZ) -> {
|
||||
samples.add(new Coordinate(sampleX, sampleZ));
|
||||
return 0.5D;
|
||||
});
|
||||
|
||||
assertEquals(3, samples.size());
|
||||
for (int index = 0; index < samples.size(); index++) {
|
||||
double angle = index * Math.PI * 2D / 3D;
|
||||
double dx = 8D * (Math.cos(angle) - Math.sin(angle));
|
||||
double dz = 8D * (Math.sin(angle) + Math.cos(angle));
|
||||
assertEquals(x + dx, samples.get(index).x, 1.0E-12D);
|
||||
assertEquals(z + dz, samples.get(index).z, 1.0E-12D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compositeSampleCachesKeepFractionalCentersSeparate() {
|
||||
NoiseProvider source = (x, z) -> Math.sin(x * 0.013D) + Math.cos(z * 0.021D);
|
||||
|
||||
for (double x : new double[]{-31.75D, -0.25D, 0.25D, 31.75D}) {
|
||||
double z = x + 7.5D;
|
||||
Map<Coordinate, Integer> samples = new HashMap<>();
|
||||
double expected = IrisInterpolation.getNoise(InterpolationMethod.STARCAST_12, x, z, 32D,
|
||||
(sampleX, sampleZ) -> IrisInterpolation.getBilinearNoise(sampleX, sampleZ, 32D, source));
|
||||
double actual = IrisInterpolation.getNoise(InterpolationMethod.BILINEAR_STARCAST_12,
|
||||
x, z, 32D, (sampleX, sampleZ) -> {
|
||||
samples.merge(new Coordinate(sampleX, sampleZ), 1, Integer::sum);
|
||||
return source.noise(sampleX, sampleZ);
|
||||
});
|
||||
|
||||
assertEquals(expected, actual, 0D);
|
||||
for (int calls : samples.values()) {
|
||||
assertEquals(1, calls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fractionalGridSpacingDoesNotCollapseOrJumpAtCellBoundaries() {
|
||||
NoiseProvider source = (x, z) -> Math.sin(x * 0.73D) * 0.4D + Math.cos(z * 0.59D) * 0.2D;
|
||||
|
||||
for (InterpolationMethod method : InterpolationMethod.values()) {
|
||||
for (double radius : new double[]{0.125D, 0.375D, 2.5D, 7.75D}) {
|
||||
for (int cell : new int[]{-7, -1, 0, 1, 7}) {
|
||||
double boundary = cell * radius;
|
||||
double center = IrisInterpolation.getNoise(method, boundary, 0.37D, radius, source);
|
||||
double before = IrisInterpolation.getNoise(method, boundary - 1.0E-8D, 0.37D, radius, source);
|
||||
double after = IrisInterpolation.getNoise(method, boundary + 1.0E-8D, 0.37D, radius, source);
|
||||
|
||||
assertTrue(method.name(), Double.isFinite(center));
|
||||
assertEquals(method.name(), center, before, 1.0E-6D);
|
||||
assertEquals(method.name(), center, after, 1.0E-6D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gridSamplesKeepTheConfiguredFractionalSpacing() {
|
||||
List<Coordinate> samples = new ArrayList<>();
|
||||
IrisInterpolation.getBilinearNoise(-0.25D, 0.125D, 0.375D, (x, z) -> {
|
||||
samples.add(new Coordinate(x, z));
|
||||
return x + z;
|
||||
});
|
||||
|
||||
assertEquals(List.of(new Coordinate(-0.375D, 0D), new Coordinate(0D, 0D),
|
||||
new Coordinate(-0.375D, 0.375D), new Coordinate(0D, 0.375D)), samples);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void starcastPreservesDoubleSamplePrecisionAndLinearFields() {
|
||||
NoiseProvider source = (x, z) -> 0.125D + x * 1.0E-11D - z * 2.0E-11D;
|
||||
|
||||
for (InterpolationMethod method : new InterpolationMethod[]{InterpolationMethod.STARCAST_3,
|
||||
InterpolationMethod.STARCAST_6, InterpolationMethod.STARCAST_9, InterpolationMethod.STARCAST_12}) {
|
||||
assertEquals(method.name(), source.noise(0.25D, -0.75D),
|
||||
IrisInterpolation.getNoise(method, 0.25D, -0.75D, 32.00000001D, source), 1.0E-16D);
|
||||
assertNotEquals(method.name(),
|
||||
IrisInterpolation.getNoise(method, 0D, 0D, 32D, source),
|
||||
IrisInterpolation.getNoise(method, 0.25D, -0.75D, 32D, source), 1.0E-13D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parametricCurvesHaveSymmetricMidpointsAndMonotonicEndpoints() {
|
||||
for (double alpha : new double[]{1D, 1.5D, 2D, 4D}) {
|
||||
assertEquals(0D, IrisInterpolation.parametric(0D, alpha), 0D);
|
||||
assertEquals(0.5D, IrisInterpolation.parametric(0.5D, alpha), 0D);
|
||||
assertEquals(1D, IrisInterpolation.parametric(1D, alpha), 0D);
|
||||
double previous = 0D;
|
||||
for (int sample = 1; sample <= 100; sample++) {
|
||||
double t = sample / 100D;
|
||||
double value = IrisInterpolation.parametric(t, alpha);
|
||||
assertTrue(value >= previous);
|
||||
assertTrue(value <= 1D);
|
||||
assertEquals(value, 1D - IrisInterpolation.parametric(1D - t, alpha), 1.0E-15D);
|
||||
previous = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void catmullRomKeepsLinearSlopesAcrossGridBoundaries() {
|
||||
NoiseProvider source = (x, z) -> x * 0.7D - z * 0.3D;
|
||||
for (double x : new double[]{-7.75D, -0.25D, 0.25D, 7.75D}) {
|
||||
assertEquals(source.noise(x, 2.75D),
|
||||
IrisInterpolation.getNoise(InterpolationMethod.CATMULL_ROM_SPLINE, x, 2.75D, 8D, source),
|
||||
1.0E-12D);
|
||||
}
|
||||
}
|
||||
|
||||
private record Coordinate(double x, double z) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
public class CNGDimensionDispatchTest {
|
||||
private static final double EPSILON = 1.0E-12D;
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void eachEntrypointUsesItsNativeDimensionWithScalingAndOpacity() {
|
||||
CoordinateNoise source = new CoordinateNoise();
|
||||
CNG noise = new CNG(new RNG(41L), source, 0.6D, 1).scale(0.375D).bake().scale(1.75D);
|
||||
double scale = 0.375D * 1.75D;
|
||||
|
||||
for (double x : new double[]{-13.25D, 0D, 17.5D}) {
|
||||
double z = x + 2.75D;
|
||||
double y = x - 4.125D;
|
||||
double expected1D = source.noise(x * scale) * 0.6D;
|
||||
double expected2D = source.noise(x * scale, z * scale) * 0.6D;
|
||||
double expected3D = source.noise(x * scale, y * scale, z * scale) * 0.6D;
|
||||
|
||||
assertEquals(expected1D, noise.noise(x), EPSILON);
|
||||
assertEquals(expected1D, noise.noiseFast1D(x), EPSILON);
|
||||
assertEquals(expected1D, noise.noise(new double[]{x}), EPSILON);
|
||||
assertEquals(expected2D, noise.noise(x, z), EPSILON);
|
||||
assertEquals(expected2D, noise.noiseFast2D(x, z), EPSILON);
|
||||
assertEquals(expected2D, noise.noise(new double[]{x, z}), EPSILON);
|
||||
assertEquals(expected2D * 2D - 1D, noise.noiseFastSigned2D(x, z), EPSILON);
|
||||
assertEquals(expected3D, noise.noise(x, y, z), EPSILON);
|
||||
assertEquals(expected3D, noise.noiseFast3D(x, y, z), EPSILON);
|
||||
assertEquals(expected3D, noise.noise(new double[]{x, y, z}), EPSILON);
|
||||
assertEquals(expected3D * 2D - 1D, noise.noiseFastSigned3D(x, y, z), EPSILON);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generatorsWithoutCoordinateScalingKeepTheirNativeInputs() {
|
||||
CoordinateNoise source = new CoordinateNoise() {
|
||||
@Override
|
||||
public boolean isNoScale() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
CNG fracture = new CNG(new RNG(2L), new CoordinateNoise(), 1D, 1);
|
||||
CNG noise = new CNG(new RNG(1L), source, 1D, 1).scale(7D).bake().fractureWith(fracture, 12D);
|
||||
|
||||
assertEquals(source.noise(3D), noise.noiseFast1D(3D), EPSILON);
|
||||
assertEquals(source.noise(3D, 7D), noise.noiseFast2D(3D, 7D), EPSILON);
|
||||
assertEquals(source.noiseSigned(3D, 7D), noise.noiseFastSigned2D(3D, 7D), EPSILON);
|
||||
assertEquals(source.noise(3D, 5D, 7D), noise.noiseFast3D(3D, 5D, 7D), EPSILON);
|
||||
assertEquals(source.noiseSigned(3D, 5D, 7D), noise.noiseFastSigned3D(3D, 5D, 7D), EPSILON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fractureSamplesAndDisplacesTheRequestedDimensions() {
|
||||
CoordinateNoise source = new CoordinateNoise();
|
||||
CoordinateNoise fractureSource = new CoordinateNoise();
|
||||
CNG fracture = new CNG(new RNG(2L), fractureSource, 1D, 1);
|
||||
CNG noise = new CNG(new RNG(1L), source, 0.7D, 1).scale(0.35D).fractureWith(fracture, 12D);
|
||||
double x = -13.25D;
|
||||
double y = 5.5D;
|
||||
double z = 37.75D;
|
||||
double displaced1D = x + (fractureSource.noise(x) - 0.5D) * 12D;
|
||||
double displacedX2D = x + (fractureSource.noise(x, z) - 0.5D) * 12D;
|
||||
double displacedZ2D = z + (fractureSource.noise(z, x) - 0.5D) * 12D;
|
||||
double displacedX3D = x + (fractureSource.noise(x, y, z) - 0.5D) * 12D;
|
||||
double displacedY3D = y + (fractureSource.noise(y, x) - 0.5D) * 12D;
|
||||
double displacedZ3D = z + (fractureSource.noise(z, x, y) - 0.5D) * 12D;
|
||||
double expected1D = source.noise(displaced1D * 0.35D) * 0.7D;
|
||||
double expected2D = source.noise(displacedX2D * 0.35D, displacedZ2D * 0.35D) * 0.7D;
|
||||
double expected3D = source.noise(displacedX3D * 0.35D, displacedY3D * 0.35D, displacedZ3D * 0.35D) * 0.7D;
|
||||
|
||||
assertEquals(expected1D, noise.noiseFast1D(x), EPSILON);
|
||||
assertEquals(expected1D, noise.noise(new double[]{x}), EPSILON);
|
||||
assertEquals(expected2D, noise.noiseFast2D(x, z), EPSILON);
|
||||
assertEquals(expected2D, noise.noise(new double[]{x, z}), EPSILON);
|
||||
assertEquals(expected2D * 2D - 1D, noise.noiseFastSigned2D(x, z), EPSILON);
|
||||
assertEquals(expected3D, noise.noiseFast3D(x, y, z), EPSILON);
|
||||
assertEquals(expected3D, noise.noise(new double[]{x, y, z}), EPSILON);
|
||||
assertEquals(expected3D * 2D - 1D, noise.noiseFastSigned3D(x, y, z), EPSILON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void offsetsPreserveTheNativeCoordinatePlane() {
|
||||
long seed = 75L;
|
||||
RNG random = new RNG(seed);
|
||||
double offsetX = random.nextInt(Short.MIN_VALUE, Short.MAX_VALUE);
|
||||
double offsetZ = random.nextInt(Short.MIN_VALUE, Short.MAX_VALUE);
|
||||
CoordinateNoise source = new CoordinateNoise();
|
||||
CNG noise = new CNG(new RNG(9L), source.offset(seed), 1D, 1);
|
||||
|
||||
assertEquals(source.noise(3D + offsetX), noise.noise(3D), EPSILON);
|
||||
assertEquals(source.noise(3D + offsetX, 7D + offsetZ), noise.noise(3D, 7D), EPSILON);
|
||||
assertEquals(source.noiseSigned(3D + offsetX, 7D + offsetZ), noise.noiseFastSigned2D(3D, 7D), EPSILON);
|
||||
assertEquals(source.noise(3D + offsetX, 5D, 7D + offsetZ), noise.noise(3D, 5D, 7D), EPSILON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zoomResizesSharedFracturesAndChildrenOnce() {
|
||||
CNG shared = new CNG(new RNG(71L), new CoordinateNoise(), 1D, 1).scale(0.7D);
|
||||
CNG noise = new CNG(new RNG(73L), new CoordinateNoise(), 1D, 1)
|
||||
.scale(0.8D).fractureWith(shared, 3D).child(shared);
|
||||
double one = noise.noise(7D);
|
||||
double two = noise.noise(7D, -11D);
|
||||
double three = noise.noise(7D, 17D, -11D);
|
||||
noise.zoom(2D);
|
||||
assertEquals(one, noise.noise(14D), EPSILON);
|
||||
assertEquals(two, noise.noise(14D, -22D), EPSILON);
|
||||
assertEquals(three, noise.noise(14D, 34D, -22D), EPSILON);
|
||||
assertEquals(two * 2D - 1D, noise.noiseFastSigned2D(14D, -22D), EPSILON);
|
||||
assertEquals(three * 2D - 1D, noise.noiseFastSigned3D(14D, 34D, -22D), EPSILON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zoomInvalidatesBakedAndSignedSamples() {
|
||||
CoordinateNoise source = new CoordinateNoise();
|
||||
CNG noise = new CNG(new RNG(31L), source, 1D, 1);
|
||||
noise.cached(4, "zoom", temporaryFolder.getRoot(), true);
|
||||
noise.noiseFastSigned2D(2D, 2D);
|
||||
noise.zoom(2D);
|
||||
assertEquals(source.noise(1D, 1D), noise.noise(2D, 2D), EPSILON);
|
||||
assertEquals(source.noiseSigned(1D, 1D), noise.noiseFastSigned2D(2D, 2D), EPSILON);
|
||||
for (double factor : new double[]{0D, -1D, Double.NaN, Double.POSITIVE_INFINITY, Double.MIN_VALUE}) {
|
||||
assertThrows(IllegalArgumentException.class, () -> noise.zoom(factor));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cachedIntegerAndUncachedFractionalSamplesUseTheSamePlane() {
|
||||
CoordinateNoise source = new CoordinateNoise();
|
||||
CNG noise = new CNG(new RNG(15L), source, 1D, 1);
|
||||
noise.noiseFastSigned2D(1D, 2D);
|
||||
noise.cached(4, "dimensions", temporaryFolder.getRoot(), true);
|
||||
double cached = (float) source.noise(1D, 2D);
|
||||
|
||||
assertEquals(cached, noise.noise(1D, 2D), 0D);
|
||||
assertEquals(cached, noise.noiseFast2D(1D, 2D), 0D);
|
||||
assertEquals(cached, noise.noise(new double[]{1D, 2D}), 0D);
|
||||
assertEquals(cached * 2D - 1D, noise.noiseFastSigned2D(1D, 2D), 0D);
|
||||
assertEquals(source.noise(1.25D, 2.5D), noise.noiseFast2D(1.25D, 2.5D), EPSILON);
|
||||
assertEquals(source.noise(1D), noise.noise(1D), EPSILON);
|
||||
assertEquals(source.noise(1D, 3D, 2D), noise.noise(1D, 3D, 2D), EPSILON);
|
||||
|
||||
noise.scale(0.5D);
|
||||
assertEquals(source.noise(0.5D, 1D), noise.noiseFast2D(1D, 2D), EPSILON);
|
||||
assertEquals(source.noiseSigned(0.5D, 1D), noise.noiseFastSigned2D(1D, 2D), EPSILON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cachedNoiseDoesNotMirrorOrRepeatOutsideItsBakedArea() {
|
||||
CNG uncached = new CNG(new RNG(15L), new CoordinateNoise(), 1D, 1);
|
||||
CNG cached = new CNG(new RNG(15L), new CoordinateNoise(), 1D, 1);
|
||||
cached.cached(4, "bounded-dimensions", temporaryFolder.getRoot(), true);
|
||||
double[] coordinates = {-8D, -4D, -1D, -0.25D, 0D, 0.25D, 3D, 3.75D, 4D, 4.25D, 8D, 29_999_999D};
|
||||
|
||||
for (double x : coordinates) {
|
||||
assertEquals(uncached.noise(x), cached.noise(x), 0D);
|
||||
assertEquals(uncached.noiseFast1D(x), cached.noiseFast1D(x), 0D);
|
||||
assertEquals(uncached.noise(new double[]{x}), cached.noise(new double[]{x}), 0D);
|
||||
|
||||
for (double z : coordinates) {
|
||||
double expected = uncached.noise(x, z);
|
||||
if (x >= 0D && x < 4D && z >= 0D && z < 4D && x == (int) x && z == (int) z) {
|
||||
expected = (float) expected;
|
||||
}
|
||||
|
||||
assertEquals(expected, cached.noise(x, z), 0D);
|
||||
assertEquals(expected, cached.noiseFast2D(x, z), 0D);
|
||||
assertEquals(expected, cached.noise(new double[]{x, z}), 0D);
|
||||
assertEquals(expected * 2D - 1D, cached.noiseFastSigned2D(x, z), EPSILON);
|
||||
assertEquals(uncached.noise(x, 5D, z), cached.noise(x, 5D, z), 0D);
|
||||
assertEquals(uncached.noiseFast3D(x, 5D, z), cached.noiseFast3D(x, 5D, z), 0D);
|
||||
assertEquals(uncached.noiseFastSigned3D(x, 5D, z), cached.noiseFastSigned3D(x, 5D, z), 0D);
|
||||
assertEquals(uncached.noise(new double[]{x, 5D, z}), cached.noise(new double[]{x, 5D, z}), 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void octaveChangesReachNestedOffsetsAndInvalidateSignedSamples() {
|
||||
OctaveCoordinateNoise source = new OctaveCoordinateNoise();
|
||||
OffsetNoiseGenerator offset = source.offset(6L).offset(9L);
|
||||
CNG noise = new CNG(new RNG(77L), offset, 1D, 3);
|
||||
|
||||
assertEquals(3, source.octaves);
|
||||
double previous2D = noise.noiseFastSigned2D(3D, 7D);
|
||||
double previous3D = noise.noiseFastSigned3D(3D, 5D, 7D);
|
||||
noise.oct(5);
|
||||
|
||||
assertEquals(5, source.octaves);
|
||||
assertEquals(offset.noiseSigned(3D, 7D), noise.noiseFastSigned2D(3D, 7D), EPSILON);
|
||||
assertEquals(offset.noiseSigned(3D, 5D, 7D), noise.noiseFastSigned3D(3D, 5D, 7D), EPSILON);
|
||||
assertNotEquals(previous2D, noise.noiseFastSigned2D(3D, 7D), EPSILON);
|
||||
assertNotEquals(previous3D, noise.noiseFastSigned3D(3D, 5D, 7D), EPSILON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transformChangesInvalidateSignedSamples() {
|
||||
CNG noise = new CNG(new RNG(34L), new CoordinateNoise(), 1D, 1);
|
||||
double previous2D = noise.noiseFastSigned2D(3D, 7D);
|
||||
double previous3D = noise.noiseFastSigned3D(3D, 5D, 7D);
|
||||
noise.up(0.125D);
|
||||
|
||||
assertEquals(previous2D + 0.25D, noise.noiseFastSigned2D(3D, 7D), EPSILON);
|
||||
assertEquals(previous3D + 0.25D, noise.noiseFastSigned3D(3D, 5D, 7D), EPSILON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidDimensionCountsFailExplicitly() {
|
||||
CNG noise = new CNG(new RNG(9L), new CoordinateNoise(), 1D, 1);
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> noise.noise(new double[0]));
|
||||
assertThrows(IllegalArgumentException.class, () -> noise.noise(new double[]{1D, 2D, 3D, 4D}));
|
||||
}
|
||||
|
||||
private static class CoordinateNoise implements NoiseGenerator {
|
||||
@Override
|
||||
public double noise(double x) {
|
||||
return 0.125D + x * 0.001D;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double z) {
|
||||
return 0.25D + x * 0.001D + z * 0.002D;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double y, double z) {
|
||||
return 0.5D + x * 0.001D + y * 0.002D + z * 0.003D;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class OctaveCoordinateNoise extends CoordinateNoise implements OctaveNoise {
|
||||
private int octaves;
|
||||
|
||||
@Override
|
||||
public double noise(double x, double z) {
|
||||
return super.noise(x, z) / octaves;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double noise(double x, double y, double z) {
|
||||
return super.noise(x, y, z) / octaves;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOctaves(int octaves) {
|
||||
this.octaves = octaves;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,7 @@ public class CNGInjectorParityTest {
|
||||
}
|
||||
|
||||
private double legacyCombined1D(CompositeFixture fixture, double x) {
|
||||
double n = fixture.rootGenerator.noise(x, 0D, 0D);
|
||||
double n = fixture.rootGenerator.noise(x);
|
||||
double m = 1D;
|
||||
|
||||
double valueA = fixture.childA.noise(x);
|
||||
@@ -143,7 +143,7 @@ public class CNGInjectorParityTest {
|
||||
}
|
||||
|
||||
private double legacyCombined2D(CompositeFixture fixture, double x, double z) {
|
||||
double n = fixture.rootGenerator.noise(x, z, 0D);
|
||||
double n = fixture.rootGenerator.noise(x, z);
|
||||
double m = 1D;
|
||||
|
||||
double valueA = fixture.childA.noise(x, z);
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class CellularNoisePrecisionTest {
|
||||
@Test
|
||||
public void distantCellBoundariesRetainSubFloatCoordinatePrecision() {
|
||||
for (double origin : new double[]{-29_999_000D, 29_998_000D}) {
|
||||
CellularNoise noise = new CellularNoise(1337L);
|
||||
for (int dimensions = 1; dimensions <= 3; dimensions++) {
|
||||
double previousCoordinate = origin;
|
||||
double previousValue = sample(noise, dimensions, origin);
|
||||
boolean preciseBoundary = false;
|
||||
for (int step = 1; step <= 8192; step++) {
|
||||
double coordinate = origin + step * 0.125D;
|
||||
double value = sample(noise, dimensions, coordinate);
|
||||
if (Float.floatToRawIntBits((float) coordinate)
|
||||
== Float.floatToRawIntBits((float) previousCoordinate)
|
||||
&& value != previousValue) {
|
||||
preciseBoundary = true;
|
||||
break;
|
||||
}
|
||||
previousCoordinate = coordinate;
|
||||
previousValue = value;
|
||||
}
|
||||
assertTrue("Missing precise cell boundary at " + origin + " in " + dimensions + "D", preciseBoundary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void distantSamplesAreBoundedAndRepeatableAcrossSeeds() {
|
||||
for (long seed : new long[]{0L, -1L, Long.MIN_VALUE, Long.MAX_VALUE}) {
|
||||
CellularNoise first = new CellularNoise(seed);
|
||||
CellularNoise second = new CellularNoise(seed);
|
||||
for (int dimensions = 1; dimensions <= 3; dimensions++) {
|
||||
for (int step = -256; step <= 256; step++) {
|
||||
double coordinate = Math.copySign(29_999_000D, step) + step * 0.125D;
|
||||
double value = sample(first, dimensions, coordinate);
|
||||
assertTrue(Double.isFinite(value) && value >= 0D && value <= 1D);
|
||||
assertEquals(value, sample(second, dimensions, coordinate), 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static double sample(CellularNoise noise, int dimensions, double coordinate) {
|
||||
return switch (dimensions) {
|
||||
case 1 -> noise.noise(coordinate);
|
||||
case 2 -> noise.noise(coordinate, -29_998_713.375D);
|
||||
default -> noise.noise(coordinate, 73.375D, 29_998_713.375D);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ChladniNoiseTest {
|
||||
@Test
|
||||
public void horizontalPlateHasMirroredNodalLinesAndDarkAntinodes() {
|
||||
for (long seed : new long[]{0L, 1337L, Long.MIN_VALUE}) {
|
||||
ChladniNoise noise = new ChladniNoise(seed);
|
||||
double minimum = 1D;
|
||||
for (int index = 0; index < 256; index++) {
|
||||
double x = index * 0.031D - 4D;
|
||||
double z = index * -0.047D + 3D;
|
||||
assertEquals(1D, noise.noise(x, x), 0D);
|
||||
assertEquals(noise.noise(x, z), noise.noise(z, x), 1.0E-12D);
|
||||
minimum = Math.min(minimum, noise.noise(x, z));
|
||||
}
|
||||
assertTrue(minimum < 0.01D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void standingModesChangeContinuouslyWithHeight() {
|
||||
ChladniNoise noise = new ChladniNoise(1337L);
|
||||
double variation = 0D;
|
||||
for (int index = 0; index < 512; index++) {
|
||||
double x = index * 0.019D - 5D;
|
||||
double z = index * -0.023D + 3D;
|
||||
assertEquals(noise.noise(x, z), noise.noise(x, 0D, z), 0D);
|
||||
for (double y : new double[]{-2D, 0D, 1D, 468750D}) {
|
||||
assertEquals(noise.noise(x, y - 1.0E-8D, z),
|
||||
noise.noise(x, y + 1.0E-8D, z), 1.0E-5D);
|
||||
}
|
||||
variation += Math.abs(noise.noise(x, z) - noise.noise(x, 0.7D, z));
|
||||
}
|
||||
assertTrue(variation > 20D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upperSeedBitsChangeThePlate() {
|
||||
ChladniNoise first = new ChladniNoise(1337L);
|
||||
ChladniNoise second = new ChladniNoise(1337L + (1L << 32));
|
||||
double variation = 0D;
|
||||
for (int index = 0; index < 256; index++) {
|
||||
variation += Math.abs(first.noise(index * 0.019D) - second.noise(index * 0.019D));
|
||||
}
|
||||
assertTrue(variation > 10D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void octaveSamplesStayBoundedAndRepeatableAtWorldBorders() {
|
||||
ChladniNoise first = new ChladniNoise(Long.MAX_VALUE);
|
||||
ChladniNoise second = new ChladniNoise(Long.MAX_VALUE);
|
||||
for (int octaves : new int[]{1, 3, 16}) {
|
||||
first.setOctaves(octaves);
|
||||
second.setOctaves(octaves);
|
||||
for (int index = 0; index < 128; index++) {
|
||||
double x = -468750D + index * (937500D / 127D);
|
||||
double value = first.noise(x, x * 0.37D, x * -0.71D);
|
||||
assertTrue(Double.isFinite(value));
|
||||
assertTrue(value >= 0D && value <= 1D);
|
||||
assertEquals(value, second.noise(x, x * 0.37D, x * -0.71D), 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class CircuitNoiseTest {
|
||||
@Test
|
||||
public void tracksMeetAtTilePortsWithoutBoundaryJumps() {
|
||||
CircuitNoise noise = new CircuitNoise(0L);
|
||||
double maximum = 0D;
|
||||
for (int boundary = -8; boundary <= 8; boundary++) {
|
||||
for (int step = 0; step < 256; step++) {
|
||||
double z = step * 0.019D - 2D;
|
||||
assertEquals(noise.noise(boundary - 1E-8D, z), noise.noise(boundary + 1E-8D, z), 1E-5D);
|
||||
maximum = Math.max(maximum, noise.noise(boundary, z));
|
||||
}
|
||||
}
|
||||
assertTrue(maximum > 0.99D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void circuitHasOpenBoardTracesAndIntermediateEdges() {
|
||||
CircuitNoise noise = new CircuitNoise(1337L);
|
||||
int open = 0;
|
||||
int trace = 0;
|
||||
int edge = 0;
|
||||
for (int x = 0; x < 256; x++) {
|
||||
for (int z = 0; z < 256; z++) {
|
||||
double value = noise.noise(x / 64D, z / 64D);
|
||||
if (value == 0D) {
|
||||
open++;
|
||||
} else if (value > 0.8D) {
|
||||
trace++;
|
||||
} else {
|
||||
edge++;
|
||||
}
|
||||
}
|
||||
}
|
||||
assertTrue(open > 32768);
|
||||
assertTrue(trace > 1500);
|
||||
assertTrue(edge > 1500);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ClassicNoiseQualityTest {
|
||||
private static final long SEED = 972453L;
|
||||
|
||||
@Test
|
||||
public void directCubicUsesConfiguredSeedInBothPrecisions() {
|
||||
for (long seed : new long[]{1L, -23L, Long.MAX_VALUE}) {
|
||||
FastNoiseDouble doubles = new FastNoiseDouble(seed);
|
||||
doubles.setNoiseType(FastNoiseDouble.NoiseType.Cubic);
|
||||
FastNoise floats = new FastNoise((int) seed);
|
||||
floats.SetNoiseType(FastNoise.NoiseType.Cubic);
|
||||
for (int x = -300; x <= 300; x += 37) {
|
||||
double sampleX = x + 0.193D;
|
||||
double sampleZ = x * 0.71D + 43.517D;
|
||||
assertEquals(doubles.GetNoise(sampleX, sampleZ), doubles.GetCubic(sampleX, sampleZ), 0D);
|
||||
assertEquals(floats.GetNoise((float) sampleX, (float) sampleZ),
|
||||
floats.GetCubic((float) sampleX, (float) sampleZ), 0F);
|
||||
}
|
||||
}
|
||||
CubicNoise first = new CubicNoise(SEED);
|
||||
CubicNoise second = new CubicNoise(SEED + 1);
|
||||
assertNotEquals(first.noise(37.219D), second.noise(37.219D), 1E-8D);
|
||||
assertNotEquals(first.noise(37.219D, -91.713D), second.noise(37.219D, -91.713D), 1E-8D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void perlinDefaultsToQuinticSmoothing() {
|
||||
FastNoiseDouble source = new FastNoiseDouble(new RNG(SEED).lmax());
|
||||
source.setLongerp(FastNoiseDouble.Longerp.Qulongic);
|
||||
source.setFractalType(FastNoiseDouble.FractalType.Billow);
|
||||
source.setFractalOctaves(1);
|
||||
PerlinNoise perlin = new PerlinNoise(SEED);
|
||||
FractalBillowPerlinNoise billow = new FractalBillowPerlinNoise(SEED);
|
||||
for (int x = -240; x <= 240; x += 17) {
|
||||
double sampleX = x + 0.37D;
|
||||
double sampleZ = x * 0.91D - 8.143D;
|
||||
assertEquals(source.GetPerlin(sampleX, sampleZ), perlin.noiseSigned(sampleX, sampleZ), 0D);
|
||||
assertEquals(source.GetPerlinFractal(sampleX, sampleZ) / 2D + 0.5D,
|
||||
billow.noise(sampleX, sampleZ), 0D);
|
||||
assertEquals(source.GetPerlin(sampleX, 63.21D, sampleZ),
|
||||
perlin.noiseSigned(sampleX, 63.21D, sampleZ), 0D);
|
||||
assertEquals(source.GetPerlinFractal(sampleX, 63.21D, sampleZ) / 2D + 0.5D,
|
||||
billow.noise(sampleX, 63.21D, sampleZ), 0D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void perlinSlopesRemainContinuousAcrossLatticeBoundaries() {
|
||||
PerlinNoise noise = new PerlinNoise(SEED);
|
||||
double step = 0.001D;
|
||||
for (int x = -400; x <= 400; x += 100) {
|
||||
double center = noise.noiseSigned(x, 37.219D, -91.713D);
|
||||
double leftSlope = (center - noise.noiseSigned(x - step, 37.219D, -91.713D)) / step;
|
||||
double rightSlope = (noise.noiseSigned(x + step, 37.219D, -91.713D) - center) / step;
|
||||
assertEquals(leftSlope, rightSlope, 1E-8D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ridgedSimplexNormalizesTheActualOctaveRange() {
|
||||
FractalRigidMultiSimplexNoise ridge = new FractalRigidMultiSimplexNoise(SEED);
|
||||
FastNoiseDouble source = new FastNoiseDouble(new RNG(SEED).lmax());
|
||||
source.setFractalType(FastNoiseDouble.FractalType.RigidMulti);
|
||||
for (int octaves : new int[]{1, 2, 4, 8, 16}) {
|
||||
ridge.setOctaves(octaves);
|
||||
source.setFractalOctaves(octaves);
|
||||
double amplitudeSum = 2D - Math.scalb(1D, 1 - octaves);
|
||||
assertEquals(0D, ridge.f(1D - amplitudeSum), 1E-15D);
|
||||
assertEquals(1D, ridge.f(1D), 0D);
|
||||
for (int x = -400; x <= 400; x += 11) {
|
||||
double sampleX = x + 0.37D;
|
||||
double sampleZ = x * 0.91D - 8.143D;
|
||||
double value = ridge.noise(sampleX, sampleZ);
|
||||
double raw = source.GetSimplexFractal(sampleX, sampleZ);
|
||||
assertEquals((raw + amplitudeSum - 1D) / amplitudeSum, value, 1E-15D);
|
||||
assertTrue(value >= 0D && value <= 1D);
|
||||
assertEquals(value * 2D - 1D, ridge.noiseSigned(sampleX, sampleZ), 0D);
|
||||
}
|
||||
}
|
||||
ridge.setOctaves(1);
|
||||
assertEquals(1D - Math.abs(source.GetSimplex(37.219D, -91.713D)),
|
||||
ridge.noise(37.219D, -91.713D), 0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void classicAndCellularFamiliesRemainBoundedAtWorldEdges() {
|
||||
NoiseGenerator[] generators = {
|
||||
new CubicNoise(SEED), new FractalCubicNoise(SEED), new FractalBillowPerlinNoise(SEED),
|
||||
new FractalBillowSimplexNoise(SEED), new FractalFBMSimplexNoise(SEED),
|
||||
new FractalRigidMultiSimplexNoise(SEED), new GlobNoise(SEED), new CellHeightNoise(SEED),
|
||||
new VascularNoise(SEED), new CellularNoise(SEED)
|
||||
};
|
||||
for (NoiseGenerator generator : generators) {
|
||||
for (int octave : new int[]{1, 4, 16}) {
|
||||
if (generator instanceof OctaveNoise fractal) {
|
||||
fractal.setOctaves(octave);
|
||||
}
|
||||
for (double origin : new double[]{-30_000_000D, 0D, 30_000_000D}) {
|
||||
for (int i = 0; i < 64; i++) {
|
||||
double x = origin + i * 0.317D;
|
||||
double z = -origin - i * 0.913D;
|
||||
assertBounded(generator.noise(x));
|
||||
assertBounded(generator.noise(x, z));
|
||||
assertBounded(generator.noise(x, 71.917D, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cellularHeightAndVascularKeepComplementaryProfiles() {
|
||||
CellHeightNoise height = new CellHeightNoise(SEED);
|
||||
VascularNoise vascular = new VascularNoise(SEED);
|
||||
for (int x = -300; x <= 300; x += 13) {
|
||||
double sampleX = x + 0.91D;
|
||||
double sampleZ = x * 0.31D - 7.193D;
|
||||
assertEquals(1D, height.noise(sampleX, sampleZ) + vascular.noise(sampleX, sampleZ), 1E-15D);
|
||||
assertEquals(1D, height.noise(sampleX, 87.137D, sampleZ)
|
||||
+ vascular.noise(sampleX, 87.137D, sampleZ), 1E-15D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vascularBrightensWhereCellDistancesConverge() {
|
||||
FastNoiseDouble distance = new FastNoiseDouble(new RNG(SEED).lmax());
|
||||
distance.setCellularDistanceFunction(FastNoiseDouble.CellularDistanceFunction.Natural);
|
||||
distance.setCellularReturnType(FastNoiseDouble.CellularReturnType.Distance2Sub);
|
||||
CellHeightNoise height = new CellHeightNoise(SEED);
|
||||
VascularNoise vascular = new VascularNoise(SEED);
|
||||
int borderSamples = 0;
|
||||
int interiorSamples = 0;
|
||||
for (int x = -500; x <= 500; x++) {
|
||||
double sampleX = x + 0.137D;
|
||||
double sampleZ = x * 3.17D - 71.913D;
|
||||
double separation = distance.GetCellular(sampleX, sampleZ) + 1D;
|
||||
if (separation < 0.02D) {
|
||||
assertTrue(vascular.noise(sampleX, sampleZ) > 0.99D);
|
||||
assertTrue(height.noise(sampleX, sampleZ) < 0.01D);
|
||||
borderSamples++;
|
||||
} else if (separation > 1D) {
|
||||
assertTrue(vascular.noise(sampleX, sampleZ) < 0.5D);
|
||||
assertTrue(height.noise(sampleX, sampleZ) > 0.5D);
|
||||
interiorSamples++;
|
||||
}
|
||||
assertEquals(vascular.noise(sampleX, sampleZ) * 2D - 1D,
|
||||
vascular.noiseSigned(sampleX, sampleZ), 2E-16D);
|
||||
}
|
||||
assertTrue(borderSamples > 0);
|
||||
assertTrue(interiorSamples > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fractalConstructorsStartAtOneOctave() {
|
||||
NoiseGenerator[] generators = {
|
||||
new FractalCubicNoise(SEED), new FractalFBMSimplexNoise(SEED),
|
||||
new FractalBillowPerlinNoise(SEED), new FractalBillowSimplexNoise(SEED),
|
||||
new FractalRigidMultiSimplexNoise(SEED)
|
||||
};
|
||||
for (NoiseGenerator generator : generators) {
|
||||
double initial = generator.noise(37.219D, -91.713D, 17.731D);
|
||||
((OctaveNoise) generator).setOctaves(1);
|
||||
assertEquals(initial, generator.noise(37.219D, -91.713D, 17.731D), 0D);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertBounded(double value) {
|
||||
assertTrue(Double.isFinite(value) && value >= 0D && value <= 1D);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class CloverNoiseBoundaryTest {
|
||||
@Test
|
||||
public void threeDimensionalNoiseRemainsContinuousAcrossZeroZ() {
|
||||
for (long seed : new long[]{0L, 1337L, Long.MIN_VALUE}) {
|
||||
CloverNoise noise = new CloverNoise(seed);
|
||||
for (double x = -3.125D; x < 4D; x += 0.375D) {
|
||||
double atZero = noise.noise(x, 0.417D, 0D);
|
||||
assertEquals(atZero, noise.noise(x, 0.417D, -1E-7D), 1E-5D);
|
||||
assertEquals(atZero, noise.noise(x, 0.417D, 1E-7D), 1E-5D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout = 5000)
|
||||
public void fractalIterationsCannotDivideByZeroOrRunUnbounded() {
|
||||
CloverNoise.Noise2D twoDimensional = new CloverNoise.Noise2D(1337L);
|
||||
CloverNoise.Noise3D threeDimensional = new CloverNoise.Noise3D(1337L);
|
||||
assertEquals(twoDimensional.noise(0.25D, -0.75D),
|
||||
twoDimensional.fractalNoise(0.25D, -0.75D, 0), 0D);
|
||||
assertEquals(threeDimensional.noise(0.25D, 0.5D, -0.75D),
|
||||
threeDimensional.fractalNoise(0.25D, 0.5D, -0.75D, Integer.MIN_VALUE), 0D);
|
||||
double bounded2D = twoDimensional.fractalNoise(0.25D, -0.75D, 16);
|
||||
double bounded3D = threeDimensional.fractalNoise(0.25D, 0.5D, -0.75D, 16);
|
||||
assertEquals(bounded2D, twoDimensional.fractalNoise(0.25D, -0.75D, Integer.MAX_VALUE), 0D);
|
||||
assertEquals(bounded3D, threeDimensional.fractalNoise(0.25D, 0.5D, -0.75D, Integer.MAX_VALUE), 0D);
|
||||
assertTrue(Double.isFinite(bounded2D));
|
||||
assertTrue(Double.isFinite(bounded3D));
|
||||
}
|
||||
@Test
|
||||
public void stationaryCurlVectorsNormalizeToZero() {
|
||||
CloverNoise.Vector2 twoDimensional = new CloverNoise.Vector2().normalize();
|
||||
CloverNoise.Vector3 threeDimensional = new CloverNoise.Vector3().normalize();
|
||||
assertEquals(0D, twoDimensional.getX(), 0D);
|
||||
assertEquals(0D, twoDimensional.getY(), 0D);
|
||||
assertEquals(0D, threeDimensional.getX(), 0D);
|
||||
assertEquals(0D, threeDimensional.getY(), 0D);
|
||||
assertEquals(0D, threeDimensional.getZ(), 0D);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,13 @@ package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class CloverNoiseParityTest {
|
||||
@@ -40,4 +47,59 @@ public class CloverNoiseParityTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void threeDimensionalCoordinateGridPreservesExactNoiseBits() {
|
||||
long[] expected = {-4534805072329300766L, 938556028071277557L, -6813256383186365522L,
|
||||
-4452859942568220233L, 5365194784097108235L};
|
||||
|
||||
for (int index = 0; index < SEEDS.length; index++) {
|
||||
assertEquals("seed=" + SEEDS[index], expected[index], coordinateDigest(new CloverNoise(SEEDS[index]), 0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout = 15000)
|
||||
public void concurrentSamplingKeepsThreadScratchIndependent() throws Exception {
|
||||
CloverNoise shared = new CloverNoise(1337L);
|
||||
long[] expected = new long[8];
|
||||
for (int index = 0; index < expected.length; index++) {
|
||||
expected[index] = coordinateDigest(shared, index * 991);
|
||||
}
|
||||
|
||||
try (ExecutorService executor = Executors.newFixedThreadPool(4)) {
|
||||
List<Future<Long>> results = new ArrayList<>(expected.length);
|
||||
for (int index = 0; index < expected.length; index++) {
|
||||
int start = index * 991;
|
||||
results.add(executor.submit(() -> coordinateDigest(shared, start)));
|
||||
}
|
||||
|
||||
for (int index = 0; index < expected.length; index++) {
|
||||
assertEquals(expected[index], results.get(index).get(5, TimeUnit.SECONDS).longValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void samplingKeepsCallerVectorsUnchanged() {
|
||||
CloverNoise.Noise3D noise = new CloverNoise.Noise3D(1337L);
|
||||
CloverNoise.Vector3 point = new CloverNoise.Vector3(0.25D, 0.5D, -0.75D);
|
||||
double expected = noise.noise(point);
|
||||
noise.noise(71D, -31D, 113D);
|
||||
|
||||
assertEquals(0.25D, point.getX(), 0D);
|
||||
assertEquals(0.5D, point.getY(), 0D);
|
||||
assertEquals(-0.75D, point.getZ(), 0D);
|
||||
assertEquals(expected, noise.noise(point), 0D);
|
||||
}
|
||||
|
||||
private static long coordinateDigest(CloverNoise noise, int start) {
|
||||
long digest = 0L;
|
||||
for (int index = start; index < start + 8192; index++) {
|
||||
double x = ((index * 1580030173L) % 60000000L - 30000000L) / 64D + 0.371D;
|
||||
double y = ((index * 91437L) % 384L - 64L) / 64D + 0.231D;
|
||||
double z = ((index * 59260789L) % 60000000L - 30000000L) / 64D - 0.219D;
|
||||
digest = Long.rotateLeft(digest, 7) ^ Double.doubleToRawLongBits(noise.noise(x, y, z));
|
||||
}
|
||||
return digest;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class CraterNoiseTest {
|
||||
@Test
|
||||
public void craterCentersSitInsideRaisedSphericalRims() {
|
||||
CraterNoise noise = new CraterNoise(0L);
|
||||
double center = noise.noise(0.2D, 0.2D, 0.2D);
|
||||
|
||||
assertTrue(center < 0.4D);
|
||||
assertTrue(noise.noise(0.56D, 0.2D, 0.2D) > center + 0.2D);
|
||||
assertTrue(noise.noise(0.2D, 0.56D, 0.2D) > center + 0.2D);
|
||||
assertTrue(noise.noise(0.2D, 0.2D, 0.56D) > center + 0.2D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void neighboringCellSearchDoesNotLeaveTileSeams() {
|
||||
double epsilon = 1.0E-8D;
|
||||
for (long seed : new long[]{0L, 713L, Long.MIN_VALUE}) {
|
||||
CraterNoise noise = new CraterNoise(seed);
|
||||
for (int boundary = -3; boundary <= 3; boundary++) {
|
||||
for (int index = 0; index < 64; index++) {
|
||||
double first = index * 0.071D - 2D;
|
||||
double second = index * -0.093D + 3D;
|
||||
assertEquals(noise.noise(boundary - epsilon, first, second),
|
||||
noise.noise(boundary + epsilon, first, second), 1.0E-5D);
|
||||
assertEquals(noise.noise(first, boundary - epsilon, second),
|
||||
noise.noise(first, boundary + epsilon, second), 1.0E-5D);
|
||||
assertEquals(noise.noise(first, second, boundary - epsilon),
|
||||
noise.noise(first, second, boundary + epsilon), 1.0E-5D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void horizontalSliceHasDistinctBowlsAndAnnularRims() {
|
||||
CraterNoise noise = new CraterNoise(713L);
|
||||
CraterNoise otherSeed = new CraterNoise(714L);
|
||||
double minimum = 1D;
|
||||
double maximum = 0D;
|
||||
double heightVariation = 0D;
|
||||
double seedVariation = 0D;
|
||||
|
||||
for (int index = 0; index < 1024; index++) {
|
||||
double x = index * 0.037D - 4D;
|
||||
double z = index * -0.051D + 7D;
|
||||
double value = noise.noise(x, z);
|
||||
assertEquals(value, noise.noise(x, 0D, z), 0D);
|
||||
minimum = Math.min(minimum, value);
|
||||
maximum = Math.max(maximum, value);
|
||||
heightVariation += Math.abs(value - noise.noise(x, 1.25D, z));
|
||||
seedVariation += Math.abs(noise.noise(x) - otherSeed.noise(x));
|
||||
}
|
||||
|
||||
assertTrue(minimum < 0.3D);
|
||||
assertTrue(maximum > 0.7D);
|
||||
assertTrue(heightVariation > 10D);
|
||||
assertTrue(seedVariation > 10D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void octaveSamplesStayBoundedAndRepeatableAtWorldBorders() {
|
||||
CraterNoise first = new CraterNoise(Long.MAX_VALUE);
|
||||
CraterNoise second = new CraterNoise(Long.MAX_VALUE);
|
||||
|
||||
for (int octaves : new int[]{1, 4, 16}) {
|
||||
first.setOctaves(octaves);
|
||||
second.setOctaves(octaves);
|
||||
for (double coordinate : new double[]{-468750D, -1D, -0D, 0.125D, 1D, 468750D}) {
|
||||
double value = first.noise(coordinate, coordinate * 0.37D, coordinate * -0.71D);
|
||||
assertTrue(Double.isFinite(value));
|
||||
assertTrue(value >= 0D && value <= 1D);
|
||||
assertEquals(value, second.noise(coordinate, coordinate * 0.37D, coordinate * -0.71D), 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
-11
@@ -6,6 +6,7 @@ import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class FastNoiseDoubleCellularParityTest {
|
||||
private static final FastNoiseDouble.CellularDistanceFunction[] DISTANCE_FUNCTIONS = new FastNoiseDouble.CellularDistanceFunction[]{
|
||||
@@ -29,7 +30,7 @@ public class FastNoiseDoubleCellularParityTest {
|
||||
private static final double[] CELL_3D_Z = getDeclaredDoubleArray("CELL_3D_Z");
|
||||
|
||||
@Test
|
||||
public void cellular2DTwoEdgeModesMatchLegacyUpdateLogic() throws Exception {
|
||||
public void cellular2DTwoEdgeModesMatchExpandedReference() throws Exception {
|
||||
for (long seed = 11L; seed <= 17L; seed++) {
|
||||
double frequency = 0.037D + (seed * 0.001D);
|
||||
for (FastNoiseDouble.CellularDistanceFunction distanceFunction : DISTANCE_FUNCTIONS) {
|
||||
@@ -42,7 +43,7 @@ public class FastNoiseDoubleCellularParityTest {
|
||||
for (int y = -96; y <= 96; y += 13) {
|
||||
double sampleX = x + 0.37D;
|
||||
double sampleY = y - 0.41D;
|
||||
double expected = legacyGetCellular2D(seed, frequency, distanceFunction, returnType, sampleX, sampleY);
|
||||
double expected = referenceGetCellular2D(seed, frequency, distanceFunction, returnType, sampleX, sampleY);
|
||||
double actual = noise.GetCellular(sampleX, sampleY);
|
||||
assertEquals("2D seed=" + seed + " distance=" + distanceFunction + " return=" + returnType + " x=" + sampleX + " y=" + sampleY, expected, actual, 0D);
|
||||
}
|
||||
@@ -53,7 +54,7 @@ public class FastNoiseDoubleCellularParityTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cellular3DTwoEdgeModesMatchLegacyUpdateLogic() throws Exception {
|
||||
public void cellular3DTwoEdgeModesMatchExpandedReference() throws Exception {
|
||||
for (long seed = 31L; seed <= 35L; seed++) {
|
||||
double frequency = 0.029D + (seed * 0.001D);
|
||||
for (FastNoiseDouble.CellularDistanceFunction distanceFunction : DISTANCE_FUNCTIONS) {
|
||||
@@ -68,7 +69,7 @@ public class FastNoiseDoubleCellularParityTest {
|
||||
double sampleX = x + 0.19D;
|
||||
double sampleY = y - 0.27D;
|
||||
double sampleZ = z + 0.43D;
|
||||
double expected = legacyGetCellular3D(seed, frequency, distanceFunction, returnType, sampleX, sampleY, sampleZ);
|
||||
double expected = referenceGetCellular3D(seed, frequency, distanceFunction, returnType, sampleX, sampleY, sampleZ);
|
||||
double actual = noise.GetCellular(sampleX, sampleY, sampleZ);
|
||||
assertEquals("3D seed=" + seed + " distance=" + distanceFunction + " return=" + returnType + " x=" + sampleX + " y=" + sampleY + " z=" + sampleZ, expected, actual, 0D);
|
||||
}
|
||||
@@ -79,7 +80,44 @@ public class FastNoiseDoubleCellularParityTest {
|
||||
}
|
||||
}
|
||||
|
||||
private double legacyGetCellular2D(long seed, double frequency, FastNoiseDouble.CellularDistanceFunction distanceFunction, FastNoiseDouble.CellularReturnType returnType, double x, double y) throws Exception {
|
||||
@Test
|
||||
public void secondNearestCellMayLieOutsideImmediateNeighbors() throws Exception {
|
||||
long seed2D = 2320148477032134941L;
|
||||
long seed3D = 8394744594210714849L;
|
||||
for (FastNoiseDouble.CellularDistanceFunction distanceFunction : DISTANCE_FUNCTIONS) {
|
||||
for (FastNoiseDouble.CellularReturnType returnType : TWO_EDGE_RETURN_TYPES) {
|
||||
FastNoiseDouble noise = new FastNoiseDouble(seed2D);
|
||||
noise.setFrequency(1D);
|
||||
noise.setCellularDistanceFunction(distanceFunction);
|
||||
noise.setCellularReturnType(returnType);
|
||||
double x = 783.44443982D;
|
||||
double y = 1896.50036984D;
|
||||
assertEquals(referenceGetCellular2D(seed2D, 1D, distanceFunction, returnType, x, y),
|
||||
noise.GetCellular(x, y), 0D);
|
||||
noise.setSeed(seed3D);
|
||||
x = -2741.75935805D;
|
||||
y = -1049.4161945D;
|
||||
double z = -1528.00711843D;
|
||||
assertEquals(referenceGetCellular3D(seed3D, 1D, distanceFunction, returnType, x, y, z),
|
||||
noise.GetCellular(x, y, z), 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void distanceProfilesStayContinuousWhenSearchCenterChanges() {
|
||||
FastNoiseDouble noise = new FastNoiseDouble(2320148477032134941L);
|
||||
noise.setFrequency(1D);
|
||||
noise.setCellularDistanceFunction(FastNoiseDouble.CellularDistanceFunction.Natural);
|
||||
for (FastNoiseDouble.CellularReturnType returnType : TWO_EDGE_RETURN_TYPES) {
|
||||
noise.setCellularReturnType(returnType);
|
||||
double below = noise.GetCellular(783.44443982D, 1896.5D - 1E-8D);
|
||||
double above = noise.GetCellular(783.44443982D, 1896.5D + 1E-8D);
|
||||
assertTrue(Math.abs(above - below) < 1E-6D);
|
||||
}
|
||||
}
|
||||
|
||||
private double referenceGetCellular2D(long seed, double frequency, FastNoiseDouble.CellularDistanceFunction distanceFunction, FastNoiseDouble.CellularReturnType returnType, double x, double y) throws Exception {
|
||||
double scaledX = x * frequency;
|
||||
double scaledY = y * frequency;
|
||||
long xr = fastRound(scaledX);
|
||||
@@ -87,8 +125,8 @@ public class FastNoiseDoubleCellularParityTest {
|
||||
double distance = 999999D;
|
||||
double distance2 = 999999D;
|
||||
|
||||
for (long xi = xr - 1; xi <= xr + 1; xi++) {
|
||||
for (long yi = yr - 1; yi <= yr + 1; yi++) {
|
||||
for (long xi = xr - 2; xi <= xr + 2; xi++) {
|
||||
for (long yi = yr - 2; yi <= yr + 2; yi++) {
|
||||
int cellIndex = (int) hash2D(seed, xi, yi) & 255;
|
||||
double vecX = xi - scaledX + CELL_2D_X[cellIndex];
|
||||
double vecY = yi - scaledY + CELL_2D_Y[cellIndex];
|
||||
@@ -105,7 +143,7 @@ public class FastNoiseDoubleCellularParityTest {
|
||||
return applyTwoEdgeReturn(distance, distance2, returnType);
|
||||
}
|
||||
|
||||
private double legacyGetCellular3D(long seed, double frequency, FastNoiseDouble.CellularDistanceFunction distanceFunction, FastNoiseDouble.CellularReturnType returnType, double x, double y, double z) throws Exception {
|
||||
private double referenceGetCellular3D(long seed, double frequency, FastNoiseDouble.CellularDistanceFunction distanceFunction, FastNoiseDouble.CellularReturnType returnType, double x, double y, double z) throws Exception {
|
||||
double scaledX = x * frequency;
|
||||
double scaledY = y * frequency;
|
||||
double scaledZ = z * frequency;
|
||||
@@ -114,10 +152,11 @@ public class FastNoiseDoubleCellularParityTest {
|
||||
long zr = fastRound(scaledZ);
|
||||
double distance = 999999D;
|
||||
double distance2 = 999999D;
|
||||
int radius = distanceFunction == FastNoiseDouble.CellularDistanceFunction.Manhattan ? 3 : 2;
|
||||
|
||||
for (long xi = xr - 1; xi <= xr + 1; xi++) {
|
||||
for (long yi = yr - 1; yi <= yr + 1; yi++) {
|
||||
for (long zi = zr - 1; zi <= zr + 1; zi++) {
|
||||
for (long xi = xr - radius; xi <= xr + radius; xi++) {
|
||||
for (long yi = yr - radius; yi <= yr + radius; yi++) {
|
||||
for (long zi = zr - radius; zi <= zr + radius; zi++) {
|
||||
int cellIndex = (int) hash3D(seed, xi, yi, zi) & 255;
|
||||
double vecX = xi - scaledX + CELL_3D_X[cellIndex];
|
||||
double vecY = yi - scaledY + CELL_3D_Y[cellIndex];
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class FastNoiseFloorTest {
|
||||
@Test
|
||||
public void doubleLatticeFloorHandlesNegativeIntegersAndNeighbors() throws Exception {
|
||||
Method floor = FastNoiseDouble.class.getDeclaredMethod("fastFloor", double.class);
|
||||
floor.setAccessible(true);
|
||||
assertEquals(-1L, ((Long) floor.invoke(null, -1D)).longValue());
|
||||
assertEquals(-2L, ((Long) floor.invoke(null, -2D)).longValue());
|
||||
assertEquals(-2L, ((Long) floor.invoke(null, Math.nextDown(-1D))).longValue());
|
||||
assertEquals(-1L, ((Long) floor.invoke(null, Math.nextUp(-1D))).longValue());
|
||||
assertEquals(0L, ((Long) floor.invoke(null, -0D)).longValue());
|
||||
assertEquals(Long.MIN_VALUE, ((Long) floor.invoke(null, (double) Long.MIN_VALUE)).longValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void floatLatticeFloorHandlesNegativeIntegersAndNeighbors() throws Exception {
|
||||
Method floor = FastNoise.class.getDeclaredMethod("FastFloor", float.class);
|
||||
floor.setAccessible(true);
|
||||
assertEquals(-1, ((Integer) floor.invoke(null, -1F)).intValue());
|
||||
assertEquals(-2, ((Integer) floor.invoke(null, -2F)).intValue());
|
||||
assertEquals(-2, ((Integer) floor.invoke(null, Math.nextDown(-1F))).intValue());
|
||||
assertEquals(-1, ((Integer) floor.invoke(null, Math.nextUp(-1F))).intValue());
|
||||
assertEquals(0, ((Integer) floor.invoke(null, -0F)).intValue());
|
||||
assertEquals(Integer.MIN_VALUE, ((Integer) floor.invoke(null, (float) Integer.MIN_VALUE)).intValue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class GaborNoiseTest {
|
||||
@Test
|
||||
public void wavePacketsHaveDirectionalDetailAndQuietGaps() {
|
||||
GaborNoise noise = new GaborNoise(1337L);
|
||||
double xx = 0D;
|
||||
double xz = 0D;
|
||||
double zz = 0D;
|
||||
int quiet = 0;
|
||||
int dark = 0;
|
||||
int light = 0;
|
||||
for (int row = 0; row < 128; row++) {
|
||||
for (int column = 0; column < 128; column++) {
|
||||
double x = column * 0.0625D - 4D;
|
||||
double z = row * 0.0625D - 4D;
|
||||
double value = noise.noise(x, z);
|
||||
quiet += Math.abs(value - 0.5D) < 0.03D ? 1 : 0;
|
||||
dark += value < 0.3D ? 1 : 0;
|
||||
light += value > 0.7D ? 1 : 0;
|
||||
double dx = noise.noise(x + 0.001D, z) - noise.noise(x - 0.001D, z);
|
||||
double dz = noise.noise(x, z + 0.001D) - noise.noise(x, z - 0.001D);
|
||||
xx += dx * dx;
|
||||
xz += dx * dz;
|
||||
zz += dz * dz;
|
||||
}
|
||||
}
|
||||
double spread = Math.sqrt((xx - zz) * (xx - zz) + 4D * xz * xz);
|
||||
double major = xx + zz + spread;
|
||||
double minor = xx + zz - spread;
|
||||
assertTrue(major > minor * 3D);
|
||||
assertTrue(quiet > 1000);
|
||||
assertTrue(dark > 500);
|
||||
assertTrue(light > 500);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class GeometricNoiseTest {
|
||||
private static final double SQRT_3 = Math.sqrt(3D);
|
||||
private static final long[] SEEDS = {0L, -1L, 1337L, Long.MIN_VALUE, Long.MAX_VALUE};
|
||||
|
||||
@Test
|
||||
public void regularHexagonsMatchNearestLatticeCenters() {
|
||||
NoiseGenerator[] noises = {new HexagonNoise(177L), new HexSimplexNoise(311L)};
|
||||
for (NoiseGenerator noise : noises) {
|
||||
for (double x = -5.93D; x < 6D; x += 0.137D) {
|
||||
for (double z = -5.97D; z < 6D; z += 0.173D) {
|
||||
double bestDistance = Double.POSITIVE_INFINITY;
|
||||
double centerX = 0D;
|
||||
double centerZ = 0D;
|
||||
for (int q = -8; q <= 8; q++) {
|
||||
for (int r = -8; r <= 8; r++) {
|
||||
double candidateX = SQRT_3 * (q + (r * 0.5D));
|
||||
double candidateZ = 1.5D * r;
|
||||
double dx = x - candidateX;
|
||||
double dz = z - candidateZ;
|
||||
double distance = (dx * dx) + (dz * dz);
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
centerX = candidateX;
|
||||
centerZ = candidateZ;
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals(noise.noise(centerX, centerZ), noise.noise(x, z), 0D);
|
||||
assertEquals(noise.noise(centerX, 2.75D, centerZ), noise.noise(x, 2.75D, z), 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hexagonNeighborsFollowCoherentHeat() {
|
||||
HexagonNoise noise = new HexagonNoise(7331L);
|
||||
double adjacentDifference = 0D;
|
||||
double distantDifference = 0D;
|
||||
for (int q = -32; q <= 32; q++) {
|
||||
for (int r = -32; r <= 32; r++) {
|
||||
double x = SQRT_3 * (q + (r * 0.5D));
|
||||
double z = 1.5D * r;
|
||||
double sample = noise.noise(x, z);
|
||||
adjacentDifference += Math.abs(sample - noise.noise(x + SQRT_3, z));
|
||||
distantDifference += Math.abs(sample - noise.noise(x + (SQRT_3 * 19D), z));
|
||||
}
|
||||
}
|
||||
assertTrue(adjacentDifference < distantDifference * 0.85D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedHexagonsHaveRegularInscribedFootprints() {
|
||||
for (int child = 0; child < 7; child++) {
|
||||
double angle = Math.toRadians((child * 60D) - 90D);
|
||||
double centerX = child == 0 ? 0D : Math.cos(angle) * (2D / 3D);
|
||||
double centerZ = child == 0 ? 0D : Math.sin(angle) * (2D / 3D);
|
||||
for (int direction = 0; direction < 60; direction++) {
|
||||
double sampleAngle = Math.toRadians(direction * 6D);
|
||||
double x = centerX + (Math.cos(sampleAngle) * 0.27D);
|
||||
double z = centerZ + (Math.sin(sampleAngle) * 0.27D);
|
||||
double q = (x / SQRT_3) - (z / 3D);
|
||||
double r = 2D * z / 3D;
|
||||
assertEquals(child, HexagonalNoise.pickChildIndex(q, r));
|
||||
assertEquals(0L, HexagonalNoise.roundQ(q, r));
|
||||
assertEquals(0L, HexagonalNoise.roundR(q, r));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sierpinskiCutsTriangleCentroidsAtEveryDepth() {
|
||||
SierpinskiTriangleNoise noise = new SierpinskiTriangleNoise(91L);
|
||||
for (int depth = 0; depth < 4; depth++) {
|
||||
double size = Math.scalb(1D, -depth);
|
||||
double holeU = size / 3D;
|
||||
double holeV = size / 3D;
|
||||
assertTrue(sampleTriangle(noise, holeU, holeV) <= 0.12D);
|
||||
assertTrue(sampleTriangle(noise, 1D - size + holeU, holeV) <= 0.12D);
|
||||
assertTrue(sampleTriangle(noise, holeU, 1D - size + holeV) <= 0.12D);
|
||||
}
|
||||
assertTrue(sampleTriangle(noise, 1D / 48D, 1D / 48D) >= 0.35D);
|
||||
assertTrue(sampleTriangle(noise, 46D / 48D, 1D / 48D) >= 0.35D);
|
||||
assertTrue(sampleTriangle(noise, 1D / 48D, 46D / 48D) >= 0.35D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sierpinskiTilesAcrossNegativeCoordinatesWithExpectedArea() {
|
||||
SierpinskiTriangleNoise noise = new SierpinskiTriangleNoise(9123L);
|
||||
Random random = new Random(8211L);
|
||||
int filled = 0;
|
||||
int samples = 65536;
|
||||
for (int index = 0; index < samples; index++) {
|
||||
double u = random.nextDouble();
|
||||
double v = random.nextDouble();
|
||||
double x = 8D * (u + (v * 0.5D));
|
||||
double z = 4D * SQRT_3 * v;
|
||||
boolean inside = noise.noise(x, z) >= 0.35D;
|
||||
if (inside) {
|
||||
filled++;
|
||||
}
|
||||
assertEquals(inside, noise.noise(x - 8D, z) >= 0.35D);
|
||||
assertEquals(inside, noise.noise(x - 4D, z - (4D * SQRT_3)) >= 0.35D);
|
||||
}
|
||||
assertEquals(Math.pow(0.75D, 4), filled / (double) samples, 0.006D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dimensionalSlicesAreConsistentAndContinuous() {
|
||||
for (NoiseGenerator noise : generators(1337L)) {
|
||||
assertEquals(noise.noise(3.125D), noise.noise(3.125D, 0D), 0D);
|
||||
assertEquals(noise.noise(3.125D, -1.75D), noise.noise(3.125D, 0D, -1.75D), 0D);
|
||||
assertEquals(noise.noise(3.125D, 1.75D, 0D), noise.noise(3.125D, 1.75D, 1E-9D), 1E-7D);
|
||||
assertEquals(noise.noise(3.125D, 0D, -1.75D), noise.noise(3.125D, 1E-9D, -1.75D), 1E-7D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void seedsAndWorldBorderCoordinatesRemainDeterministicAndBounded() {
|
||||
double[][] points = {{0D, 0D, 0D}, {-29_999_984D, -64D, 29_999_984D},
|
||||
{3.125D, 17.375D, -9.25D}, {29_999_984D, 319D, -29_999_984D}};
|
||||
for (long seed : SEEDS) {
|
||||
NoiseGenerator[] first = generators(seed);
|
||||
NoiseGenerator[] second = generators(seed);
|
||||
for (int index = 0; index < first.length; index++) {
|
||||
if (first[index] instanceof OctaveNoise octaves) {
|
||||
octaves.setOctaves(16);
|
||||
((OctaveNoise) second[index]).setOctaves(16);
|
||||
}
|
||||
for (double[] point : points) {
|
||||
double value = first[index].noise(point[0], point[1], point[2]);
|
||||
assertTrue(Double.isFinite(value) && value >= 0D && value <= 1D);
|
||||
assertEquals(value, second[index].noise(point[0], point[1], point[2]), 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double sampleTriangle(SierpinskiTriangleNoise noise, double u, double v) {
|
||||
return noise.noise(8D * (u + (v * 0.5D)), 4D * SQRT_3 * v);
|
||||
}
|
||||
|
||||
private NoiseGenerator[] generators(long seed) {
|
||||
return new NoiseGenerator[]{new HexagonNoise(seed), new HexSimplexNoise(seed),
|
||||
new HexRandomSizeNoise(seed), new HexJamesNoise(seed), new SierpinskiTriangleNoise(seed)};
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import art.arcane.iris.util.project.interpolation.InterpolationMethod;
|
||||
import art.arcane.iris.util.project.interpolation.IrisInterpolation;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class InterpolatedNoiseDimensionTest {
|
||||
@Test
|
||||
public void threeDimensionalSamplesAlwaysUseTheHorizontalPlane() {
|
||||
InterpolatedNoise noise = new InterpolatedNoise(37L, NoiseType.WHITE, InterpolationMethod.BILINEAR);
|
||||
|
||||
for (double x : new double[]{-13D, 0D, 41D}) {
|
||||
for (double y : new double[]{-89D, 0D, 173D}) {
|
||||
for (double z : new double[]{-37D, 0D, 29D}) {
|
||||
assertEquals(noise.noise(x, z), noise.noise(x, y, z), 0D);
|
||||
}
|
||||
}
|
||||
assertEquals(noise.noise(x, 0D), noise.noise(x), 0D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interpolationSamplesTheNormalizedSourceAndForwardsOctaves() {
|
||||
long seed = 93L;
|
||||
NoiseType type = NoiseType.SIMPLEX;
|
||||
NoiseGenerator source = type.create(seed);
|
||||
((OctaveNoise) source).setOctaves(4);
|
||||
InterpolatedNoise noise = new InterpolatedNoise(seed, type, InterpolationMethod.BILINEAR);
|
||||
noise.setOctaves(4);
|
||||
double scale = type.getCoordinateScale();
|
||||
|
||||
for (int x = -49; x <= 49; x += 7) {
|
||||
for (int z = -51; z <= 51; z += 11) {
|
||||
double expected = IrisInterpolation.getNoise(InterpolationMethod.BILINEAR, x, z, 32D,
|
||||
(sampleX, sampleZ) -> source.noise(sampleX * scale, sampleZ * scale));
|
||||
assertEquals(expected, noise.noise(x, z), 1.0E-12D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fractionalCoordinatesReachEveryInterpolationKernel() {
|
||||
long seed = 109L;
|
||||
NoiseType type = NoiseType.SIMPLEX;
|
||||
NoiseGenerator source = type.create(seed);
|
||||
double scale = type.getCoordinateScale();
|
||||
|
||||
for (InterpolationMethod method : InterpolationMethod.values()) {
|
||||
InterpolatedNoise noise = new InterpolatedNoise(seed, type, method);
|
||||
double expected = IrisInterpolation.getNoise(method, -7.75D, 3.25D, 32D,
|
||||
(x, z) -> source.noise(x * scale, z * scale));
|
||||
double actual = noise.noise(-7.75D, 3.25D);
|
||||
|
||||
assertEquals(method.name(), expected, actual, 0D);
|
||||
assertNotEquals(method.name(), noise.noise(-7D, 3D), actual, 1.0E-9D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noiseWrappersConstrainCubicOvershootWithoutConstrainingTheKernel() {
|
||||
double overshoot = IrisInterpolation.getNoise(InterpolationMethod.BICUBIC, 16D, 16D, 32D,
|
||||
(x, z) -> x >= 0D && x <= 32D ? 1D : 0D);
|
||||
assertEquals(1.25D, overshoot, 0D);
|
||||
|
||||
for (NoiseType type : new NoiseType[]{NoiseType.WHITE, NoiseType.CELLULAR, NoiseType.CLOVER}) {
|
||||
for (InterpolationMethod method : new InterpolationMethod[]{InterpolationMethod.BICUBIC,
|
||||
InterpolationMethod.HERMITE}) {
|
||||
InterpolatedNoise noise = new InterpolatedNoise(173L, type, method);
|
||||
boolean reachesEndpoint = false;
|
||||
for (double x = -256D; x <= 256D; x += 7.375D) {
|
||||
for (double z = -256D; z <= 256D; z += 5.5D) {
|
||||
double value = noise.noise(x, z);
|
||||
assertTrue(type.name() + "/" + method.name(), value >= 0D && value <= 1D);
|
||||
reachesEndpoint |= value == 0D || value == 1D;
|
||||
}
|
||||
}
|
||||
if (type == NoiseType.WHITE) {
|
||||
assertTrue(method.name(), reachesEndpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class KaleidoscopeNoiseTest {
|
||||
@Test
|
||||
public void wedgesMirrorAndRepeatAroundTheTileCenter() {
|
||||
KaleidoscopeNoise noise = new KaleidoscopeNoise(0L);
|
||||
for (int index = 0; index < 128; index++) {
|
||||
double angle = index * 0.037D;
|
||||
double radius = 0.05D + index * (0.4D / 128D);
|
||||
double x = radius * Math.cos(angle);
|
||||
double z = radius * Math.sin(angle);
|
||||
double value = noise.noise(0.5D + x, z);
|
||||
assertEquals(value, noise.noise(0.5D + x, -z), 1.0E-12D);
|
||||
assertEquals(value, noise.noise(0.5D - z, x), 1.0E-12D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compactMotifsMeetContinuouslyAtTileAndWedgeBoundaries() {
|
||||
double epsilon = 1.0E-8D;
|
||||
for (long seed : new long[]{0L, 1337L, Long.MIN_VALUE}) {
|
||||
KaleidoscopeNoise noise = new KaleidoscopeNoise(seed);
|
||||
for (int index = 0; index < 256; index++) {
|
||||
double position = index * 0.027D - 3D;
|
||||
double height = index * 0.017D - 2D;
|
||||
assertEquals(noise.noise(-epsilon, height, position),
|
||||
noise.noise(epsilon, height, position), 1.0E-6D);
|
||||
assertEquals(noise.noise(position, height, 0.5D - epsilon),
|
||||
noise.noise(position, height, 0.5D + epsilon), 1.0E-6D);
|
||||
assertEquals(noise.noise(0.5D - epsilon, height, 0D),
|
||||
noise.noise(0.5D + epsilon, height, 0D), 1.0E-5D);
|
||||
}
|
||||
}
|
||||
KaleidoscopeNoise noise = new KaleidoscopeNoise(0L);
|
||||
for (double boundary : new double[]{-Math.PI, -Math.PI * 0.75D, 0D, Math.PI * 0.25D, Math.PI}) {
|
||||
assertEquals(noise.noise(0.5D + 0.3D * Math.cos(boundary - epsilon),
|
||||
0.3D * Math.sin(boundary - epsilon)),
|
||||
noise.noise(0.5D + 0.3D * Math.cos(boundary + epsilon),
|
||||
0.3D * Math.sin(boundary + epsilon)), 1.0E-5D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void heightAndUpperSeedBitsChangeTheMotifs() {
|
||||
KaleidoscopeNoise first = new KaleidoscopeNoise(1337L);
|
||||
KaleidoscopeNoise second = new KaleidoscopeNoise(1337L + (1L << 32));
|
||||
double heightVariation = 0D;
|
||||
double seedVariation = 0D;
|
||||
double maximum = 0D;
|
||||
for (int index = 0; index < 512; index++) {
|
||||
double x = index * 0.037D - 5D;
|
||||
double z = index * -0.023D + 3D;
|
||||
double value = first.noise(x, z);
|
||||
assertEquals(value, first.noise(x, 0D, z), 0D);
|
||||
assertEquals(value, first.noise(x, 1.0E-8D, z), 1.0E-5D);
|
||||
heightVariation += Math.abs(value - first.noise(x, 1.25D, z));
|
||||
seedVariation += Math.abs(value - second.noise(x, z));
|
||||
maximum = Math.max(maximum, value);
|
||||
}
|
||||
assertTrue(heightVariation > 10D);
|
||||
assertTrue(seedVariation > 10D);
|
||||
assertTrue(maximum > 0.7D);
|
||||
double oneDimensionalVariation = 0D;
|
||||
for (int index = 0; index < 256; index++) {
|
||||
oneDimensionalVariation += Math.abs(first.noise(index * 0.013D)
|
||||
- second.noise(index * 0.013D));
|
||||
}
|
||||
assertTrue(oneDimensionalVariation > 10D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void octaveSamplesStayBoundedAndRepeatableAtWorldBorders() {
|
||||
KaleidoscopeNoise first = new KaleidoscopeNoise(Long.MAX_VALUE);
|
||||
KaleidoscopeNoise second = new KaleidoscopeNoise(Long.MAX_VALUE);
|
||||
for (int octaves : new int[]{1, 3, 16}) {
|
||||
first.setOctaves(octaves);
|
||||
second.setOctaves(octaves);
|
||||
for (int index = 0; index < 128; index++) {
|
||||
double x = -468750D + index * (937500D / 127D);
|
||||
double value = first.noise(x, x * 0.37D, x * -0.71D);
|
||||
assertTrue(Double.isFinite(value));
|
||||
assertTrue(value >= 0D && value <= 1D);
|
||||
assertEquals(value, second.noise(x, x * 0.37D, x * -0.71D), 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class MarbleNoiseTest {
|
||||
@Test
|
||||
public void narrowVeinsCrossPredominantlySoftStone() {
|
||||
MarbleNoise noise = new MarbleNoise(1337L);
|
||||
int dark = 0;
|
||||
int soft = 0;
|
||||
double minimum = 1D;
|
||||
double maximum = 0D;
|
||||
for (int row = 0; row < 128; row++) {
|
||||
for (int column = 0; column < 128; column++) {
|
||||
double value = noise.noise(column * 0.0625D - 4D, row * 0.0625D - 4D);
|
||||
dark += value < 0.3D ? 1 : 0;
|
||||
soft += value > 0.65D ? 1 : 0;
|
||||
minimum = Math.min(minimum, value);
|
||||
maximum = Math.max(maximum, value);
|
||||
}
|
||||
}
|
||||
assertTrue(minimum < 0.15D);
|
||||
assertTrue(maximum > 0.85D);
|
||||
assertTrue(dark > 250 && dark < 2500);
|
||||
assertTrue(soft > 10000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class MaterialPatternNoiseTest {
|
||||
@Test
|
||||
public void materialPatternsStayBoundedSeededAndVolumetric() {
|
||||
for (long seed : new long[]{0L, 813753L, Long.MIN_VALUE, Long.MAX_VALUE}) {
|
||||
PatternNoise[] patterns = patterns(seed);
|
||||
PatternNoise[] repeated = patterns(seed);
|
||||
PatternNoise[] otherSeed = patterns(seed ^ (1L << 47));
|
||||
for (int pattern = 0; pattern < patterns.length; pattern++) {
|
||||
PatternNoise noise = patterns[pattern];
|
||||
double seedDifference = 0D;
|
||||
double heightDifference = 0D;
|
||||
for (int octaves : new int[]{1, 4, 16}) {
|
||||
noise.setOctaves(octaves);
|
||||
repeated[pattern].setOctaves(octaves);
|
||||
otherSeed[pattern].setOctaves(octaves);
|
||||
for (double origin : new double[]{-468750D, 0D, 468750D}) {
|
||||
for (int i = 0; i < 128; i++) {
|
||||
double x = origin + i * 0.173D;
|
||||
double y = i * 0.071D - 4D;
|
||||
double z = -origin + i * 0.319D;
|
||||
double value = noise.noise(x, y, z);
|
||||
assertTrue(Double.isFinite(value) && value >= 0D && value <= 1D);
|
||||
assertEquals(value, repeated[pattern].noise(x, y, z), 0D);
|
||||
assertEquals(noise.noise(x, z), noise.noise(x, 0D, z), 0D);
|
||||
seedDifference += Math.abs(value - otherSeed[pattern].noise(x, y, z));
|
||||
heightDifference += Math.abs(value - noise.noise(x, y + 0.713D, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
assertTrue(noise.getClass().getSimpleName(), seedDifference > 10D);
|
||||
assertTrue(noise.getClass().getSimpleName(), heightDifference > 10D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void materialPatternsHaveNoCoordinateBoundaryJumps() {
|
||||
for (long seed : new long[]{0L, 813753L, Long.MAX_VALUE}) {
|
||||
for (PatternNoise noise : patterns(seed)) {
|
||||
for (double boundary : new double[]{-468750D, -2D, 0D, 3D, 468750D}) {
|
||||
for (int i = 0; i < 128; i++) {
|
||||
double coordinate = i * 0.073D - 4D;
|
||||
assertEquals(noise.noise(boundary - 1E-7D, 0.317D, coordinate),
|
||||
noise.noise(boundary + 1E-7D, 0.317D, coordinate), 1E-4D);
|
||||
assertEquals(noise.noise(coordinate, 0.317D, boundary - 1E-7D),
|
||||
noise.noise(coordinate, 0.317D, boundary + 1E-7D), 1E-4D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void duneWindwardSlopeIsLongerAndHornsBendDownwind() {
|
||||
DuneNoise dune = new DuneNoise(0L);
|
||||
assertTrue(dune.noise(0.02D, 0.2D) > dune.noise(0.38D, 0.2D) + 0.2D);
|
||||
double across = 0.2D + 0.32D * 0.65D;
|
||||
double crest = 0.2D + 0.43D * 0.65D * 0.65D;
|
||||
assertTrue(dune.noise(crest, across) > dune.noise(0.2D, across) + 0.05D);
|
||||
assertTrue(dune.noise(0.2D, 0.2D) > dune.noise(crest, across));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void woodKnotsCarryClosedGrowthRings() {
|
||||
WoodNoise wood = new WoodNoise(0L);
|
||||
double minimum = 1D;
|
||||
double maximum = 0D;
|
||||
for (int i = 0; i < 64; i++) {
|
||||
double angle = i * Math.PI / 32D;
|
||||
double across = 0.3D + Math.cos(angle) * 0.12D;
|
||||
double along = 0.3D + Math.sin(angle) * 0.12D;
|
||||
double x = across - 0.11D * Math.sin(along * 0.85D);
|
||||
double value = wood.noise(x, along / 0.45D);
|
||||
minimum = Math.min(minimum, value);
|
||||
maximum = Math.max(maximum, value);
|
||||
}
|
||||
double brightRingX = 0.34D - 0.11D * Math.sin(0.3D * 0.85D);
|
||||
assertTrue(maximum - minimum < 0.22D);
|
||||
assertTrue(wood.noise(brightRingX, 0.3D / 0.45D) > maximum + 0.4D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void strataContainsRepeatedHighAndLowBands() {
|
||||
StrataNoise strata = new StrataNoise(813753L);
|
||||
int highBands = 0;
|
||||
int lowBands = 0;
|
||||
double previous = strata.noise(0D, -8D);
|
||||
double current = strata.noise(0D, -8D + 0.008D);
|
||||
for (int i = 2; i < 2048; i++) {
|
||||
double next = strata.noise(0D, -8D + i * 0.008D);
|
||||
if (current > previous && current > next && current > 0.75D) {
|
||||
highBands++;
|
||||
}
|
||||
if (current < previous && current < next && current < 0.25D) {
|
||||
lowBands++;
|
||||
}
|
||||
previous = current;
|
||||
current = next;
|
||||
}
|
||||
assertTrue(highBands >= 6);
|
||||
assertTrue(lowBands >= 6);
|
||||
}
|
||||
|
||||
private static PatternNoise[] patterns(long seed) {
|
||||
return new PatternNoise[]{new DuneNoise(seed), new StrataNoise(seed), new WoodNoise(seed)};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class MengerSpongeNoiseTest {
|
||||
@Test
|
||||
public void threeSubdivisionsRetainExactlyTwentyCubedCells() {
|
||||
MengerSpongeNoise noise = new MengerSpongeNoise(0L);
|
||||
int retained = 0;
|
||||
for (int x = 0; x < 27; x++) {
|
||||
for (int y = 0; y < 27; y++) {
|
||||
for (int z = 0; z < 27; z++) {
|
||||
retained += (int) noise.noise((x + 0.5D) / 27D,
|
||||
(y + 0.5D) / 27D, (z + 0.5D) / 27D);
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals(8000, retained);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void faceSliceContainsThreeLevelsOfSquareHoles() {
|
||||
MengerSpongeNoise noise = new MengerSpongeNoise(0L);
|
||||
assertEquals(0D, noise.noise(0.5D, 0D, 0.5D), 0D);
|
||||
assertEquals(0D, noise.noise(1D / 6D, 0D, 1D / 6D), 0D);
|
||||
assertEquals(0D, noise.noise(1D / 18D, 0D, 1D / 18D), 0D);
|
||||
assertEquals(1D, noise.noise(1D / 54D, 0D, 1D / 54D), 0D);
|
||||
assertEquals(0D, noise.noise(0.5D, 0.5D, 0.05D), 0D);
|
||||
assertEquals(0D, noise.noise(0.05D, 0.5D, 0.5D), 0D);
|
||||
int retained = 0;
|
||||
for (int x = 0; x < 27; x++) {
|
||||
for (int z = 0; z < 27; z++) {
|
||||
retained += (int) noise.noise((x + 0.5D) / 27D, (z + 0.5D) / 27D);
|
||||
}
|
||||
}
|
||||
assertEquals(512, retained);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void negativeTilesRepeatAndAllAxesAndUpperSeedBitsAffectGeometry() {
|
||||
MengerSpongeNoise first = new MengerSpongeNoise(1337L);
|
||||
MengerSpongeNoise second = new MengerSpongeNoise(1337L + (1L << 32));
|
||||
double xVariation = 0D;
|
||||
double yVariation = 0D;
|
||||
double zVariation = 0D;
|
||||
double seedVariation = 0D;
|
||||
for (int index = 0; index < 512; index++) {
|
||||
double x = index * 0.019D - 4D;
|
||||
double z = index * -0.031D + 2D;
|
||||
double value = first.noise(x, z);
|
||||
assertEquals(value, first.noise(x, 0D, z), 0D);
|
||||
assertEquals(value, first.noise(x - 2D, 0D, z + 3D), 0D);
|
||||
xVariation += Math.abs(value - first.noise(x + 0.137D, z));
|
||||
yVariation += Math.abs(value - first.noise(x, 0.419D, z));
|
||||
zVariation += Math.abs(value - first.noise(x, z + 0.237D));
|
||||
seedVariation += Math.abs(value - second.noise(x, z));
|
||||
}
|
||||
assertTrue(xVariation > 20D);
|
||||
assertTrue(yVariation > 20D);
|
||||
assertTrue(zVariation > 20D);
|
||||
assertTrue(seedVariation > 20D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void octaveSamplesStayBoundedAndRepeatableAtWorldBorders() {
|
||||
MengerSpongeNoise first = new MengerSpongeNoise(Long.MAX_VALUE);
|
||||
MengerSpongeNoise second = new MengerSpongeNoise(Long.MAX_VALUE);
|
||||
for (int octaves : new int[]{1, 3, 16}) {
|
||||
first.setOctaves(octaves);
|
||||
second.setOctaves(octaves);
|
||||
for (int index = 0; index < 128; index++) {
|
||||
double x = -468750D + index * (937500D / 127D);
|
||||
double value = first.noise(x, x * 0.37D, x * -0.71D);
|
||||
assertTrue(Double.isFinite(value));
|
||||
assertTrue(value >= 0D && value <= 1D);
|
||||
assertEquals(value, second.noise(x, x * 0.37D, x * -0.71D), 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class NoiseOctaveTest {
|
||||
private static final long SEED = 972453L;
|
||||
private static final double X = 37.219D;
|
||||
private static final double Y = -91.713D;
|
||||
private static final double Z = 152.875D;
|
||||
|
||||
@Test
|
||||
public void simplexAndPerlinKeepFundamentalAndReduceDetailAmplitude() {
|
||||
for (boolean simplex : new boolean[]{false, true}) {
|
||||
NoiseGenerator noise = simplex ? new SimplexNoise(SEED) : new PerlinNoise(SEED);
|
||||
((OctaveNoise) noise).setOctaves(3);
|
||||
FastNoiseDouble source = new FastNoiseDouble(new RNG(SEED).lmax());
|
||||
source.setLongerp(FastNoiseDouble.Longerp.Qulongic);
|
||||
for (int dimensions = 1; dimensions <= 3; dimensions++) {
|
||||
double expected = 0D;
|
||||
for (int octave = 0; octave < 3; octave++) {
|
||||
double frequency = 1 << octave;
|
||||
double sample;
|
||||
if (dimensions == 3) {
|
||||
sample = simplex ? source.GetSimplex(X * frequency, Y * frequency, Z * frequency)
|
||||
: source.GetPerlin(X * frequency, Y * frequency, Z * frequency);
|
||||
} else {
|
||||
double second = dimensions == 1 ? 0D : Z * frequency;
|
||||
sample = simplex ? source.GetSimplex(X * frequency, second)
|
||||
: source.GetPerlin(X * frequency, second);
|
||||
}
|
||||
expected += sample / frequency;
|
||||
}
|
||||
expected /= 1.75D;
|
||||
double signed = switch (dimensions) {
|
||||
case 1 -> noise.noiseSigned(X);
|
||||
case 2 -> noise.noiseSigned(X, Z);
|
||||
default -> noise.noiseSigned(X, Y, Z);
|
||||
};
|
||||
double unsigned = switch (dimensions) {
|
||||
case 1 -> noise.noise(X);
|
||||
case 2 -> noise.noise(X, Z);
|
||||
default -> noise.noise(X, Y, Z);
|
||||
};
|
||||
assertEquals(expected, signed, 1E-15D);
|
||||
assertEquals(signed / 2D + 0.5D, unsigned, 0D);
|
||||
}
|
||||
assertEquals(noise.noise(X, 0D), noise.noise(X), 0D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout = 5000)
|
||||
public void octaveWrappersClampInvalidAndExcessiveCounts() {
|
||||
NoiseGenerator[] generators = {
|
||||
new SimplexNoise(SEED), new PerlinNoise(SEED), new FractalFBMSimplexNoise(SEED),
|
||||
new FractalBillowSimplexNoise(SEED), new FractalBillowPerlinNoise(SEED),
|
||||
new FractalRigidMultiSimplexNoise(SEED), new FractalCubicNoise(SEED)
|
||||
};
|
||||
for (NoiseGenerator noise : generators) {
|
||||
OctaveNoise octaveNoise = (OctaveNoise) noise;
|
||||
octaveNoise.setOctaves(1);
|
||||
double minimum = noise.noise(X, Y, Z);
|
||||
octaveNoise.setOctaves(Integer.MIN_VALUE);
|
||||
assertEquals(minimum, noise.noise(X, Y, Z), 0D);
|
||||
octaveNoise.setOctaves(16);
|
||||
double maximum = noise.noise(X, Y, Z);
|
||||
octaveNoise.setOctaves(Integer.MAX_VALUE);
|
||||
assertEquals(maximum, noise.noise(X, Y, Z), 0D);
|
||||
assertTrue(Double.isFinite(maximum));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cubicFractalUsesRequestedOctaves() {
|
||||
FractalCubicNoise noise = new FractalCubicNoise(SEED);
|
||||
noise.setOctaves(1);
|
||||
double fundamental = noise.noise(X, Y, Z);
|
||||
noise.setOctaves(4);
|
||||
double detailed = noise.noise(X, Y, Z);
|
||||
assertNotEquals(fundamental, detailed, 1E-8D);
|
||||
FastNoiseDouble source = new FastNoiseDouble(new RNG(SEED).lmax());
|
||||
source.setFractalType(FastNoiseDouble.FractalType.Billow);
|
||||
source.setFractalOctaves(4);
|
||||
assertEquals(source.GetCubicFractal(X, Y, Z) / 2D + 0.5D, detailed, 0D);
|
||||
}
|
||||
|
||||
@Test(timeout = 5000)
|
||||
public void rawFractalSamplersBoundOctaveWork() {
|
||||
FastNoiseDouble doubles = new FastNoiseDouble(SEED);
|
||||
FastNoise floats = new FastNoise((int) SEED);
|
||||
doubles.setFractalOctaves(16);
|
||||
floats.SetFractalOctaves(16);
|
||||
double expectedDouble = doubles.GetSimplexFractal(X, Y, Z);
|
||||
float expectedFloat = floats.GetSimplexFractal((float) X, (float) Y, (float) Z);
|
||||
doubles.setFractalOctaves(Long.MAX_VALUE);
|
||||
floats.SetFractalOctaves(Integer.MAX_VALUE);
|
||||
assertEquals(expectedDouble, doubles.GetSimplexFractal(X, Y, Z), 0D);
|
||||
assertEquals(expectedFloat, floats.GetSimplexFractal((float) X, (float) Y, (float) Z), 0D);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import art.arcane.iris.engine.object.NoiseStyle;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class PatternNoiseTest {
|
||||
private static final NoiseStyle[] STYLES = {
|
||||
NoiseStyle.GYROID, NoiseStyle.QUASICRYSTAL, NoiseStyle.TRUCHET,
|
||||
NoiseStyle.CRATER, NoiseStyle.VORTEX,
|
||||
NoiseStyle.DUNE, NoiseStyle.STRATA, NoiseStyle.WOOD, NoiseStyle.GABOR, NoiseStyle.MARBLE,
|
||||
NoiseStyle.SCALES, NoiseStyle.CHLADNI, NoiseStyle.KALEIDOSCOPE, NoiseStyle.MENGER_SPONGE, NoiseStyle.CIRCUIT
|
||||
};
|
||||
|
||||
@Test
|
||||
public void patternsRemainBoundedAtEveryOctaveCountAndWorldEdge() {
|
||||
Random random = new Random(1337L);
|
||||
for (long seed : new long[]{0L, 1337L, Long.MIN_VALUE, Long.MAX_VALUE}) {
|
||||
for (PatternNoise noise : patterns(seed)) {
|
||||
for (int octaves : new int[]{1, 2, 5, 16}) {
|
||||
noise.setOctaves(octaves);
|
||||
for (int sample = 0; sample < 128; sample++) {
|
||||
double x = (random.nextDouble() * 2D - 1D) * 30_000_000D / 64D;
|
||||
double y = (random.nextDouble() * 2D - 1D) * 2048D / 64D;
|
||||
double z = (random.nextDouble() * 2D - 1D) * 30_000_000D / 64D;
|
||||
assertUnit(noise, noise.noise(x));
|
||||
assertUnit(noise, noise.noise(x, z));
|
||||
assertUnit(noise, noise.noise(x, y, z));
|
||||
assertEquals(noise.noise(x, z), noise.noise(x, 0D, z), 0D);
|
||||
assertEquals(noise.noise(x), noise.noise(x, 0D), 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allPatternsUseTheFullSeedAndVaryThroughHeight() {
|
||||
PatternNoise[] lowSeed = patterns(1337L);
|
||||
PatternNoise[] sameSeed = patterns(1337L);
|
||||
PatternNoise[] highSeed = patterns(1337L + (1L << 32));
|
||||
for (int index = 0; index < lowSeed.length; index++) {
|
||||
double seedDifference = 0D;
|
||||
double heightDifference = 0D;
|
||||
for (int sample = 0; sample < 128; sample++) {
|
||||
double x = sample * 0.173D - 8D;
|
||||
double z = sample * -0.193D + 7D;
|
||||
double value = lowSeed[index].noise(x, 0.371D, z);
|
||||
assertEquals(value, sameSeed[index].noise(x, 0.371D, z), 0D);
|
||||
seedDifference += Math.abs(value - highSeed[index].noise(x, 0.371D, z));
|
||||
heightDifference += Math.abs(value - lowSeed[index].noise(x, 0.891D, z));
|
||||
}
|
||||
assertTrue(lowSeed[index].getClass().getSimpleName(), seedDifference > 1D);
|
||||
assertTrue(lowSeed[index].getClass().getSimpleName(), heightDifference > 1D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void octavesAddDetailWithoutChangingTheBaseSeed() {
|
||||
for (PatternNoise noise : patterns(42L)) {
|
||||
double[] original = new double[128];
|
||||
for (int index = 0; index < original.length; index++) {
|
||||
original[index] = noise.noise(index * 0.073D, 0.419D, index * -0.113D);
|
||||
}
|
||||
noise.setOctaves(4);
|
||||
double difference = 0D;
|
||||
for (int index = 0; index < original.length; index++) {
|
||||
difference += Math.abs(original[index] - noise.noise(index * 0.073D, 0.419D, index * -0.113D));
|
||||
}
|
||||
assertTrue(noise.getClass().getSimpleName(), difference > 1D);
|
||||
noise.setOctaves(0);
|
||||
for (int index = 0; index < original.length; index++) {
|
||||
assertEquals(original[index], noise.noise(index * 0.073D, 0.419D, index * -0.113D), 0D);
|
||||
}
|
||||
noise.setOctaves(16);
|
||||
double maximum = noise.noise(0.713D, 0.931D, -0.719D);
|
||||
noise.setOctaves(Integer.MAX_VALUE);
|
||||
assertEquals(maximum, noise.noise(0.713D, 0.931D, -0.719D), 0D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void styleIntegrationUsesTheSameScaleInEveryDimension() {
|
||||
for (NoiseStyle style : STYLES) {
|
||||
CNG direct = style.create(new RNG(7331L));
|
||||
CNG zoomed = style.style().zoomed(2D).createNoCache(new RNG(7331L), null);
|
||||
for (int index = 0; index < 128; index++) {
|
||||
double x = index * 13.713D - 400D;
|
||||
double y = index * 1.119D - 70D;
|
||||
double z = index * -17.179D + 300D;
|
||||
assertEquals(style.name(), direct.noise(x, z), zoomed.noise(x * 2D, z * 2D), 1E-10D);
|
||||
assertEquals(style.name(), direct.noise(x, y, z), zoomed.noise(x * 2D, y * 2D, z * 2D), 1E-10D);
|
||||
assertEquals(style.name(), direct.noise(x, z), direct.noise(x, 0D, z), 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static PatternNoise[] patterns(long seed) {
|
||||
return new PatternNoise[]{new GyroidNoise(seed), new QuasicrystalNoise(seed),
|
||||
new TruchetNoise(seed), new CraterNoise(seed), new VortexNoise(seed),
|
||||
new DuneNoise(seed), new StrataNoise(seed), new WoodNoise(seed), new GaborNoise(seed), new MarbleNoise(seed),
|
||||
new ScalesNoise(seed), new ChladniNoise(seed), new KaleidoscopeNoise(seed), new MengerSpongeNoise(seed), new CircuitNoise(seed)};
|
||||
}
|
||||
|
||||
private static void assertUnit(PatternNoise noise, double value) {
|
||||
assertTrue(noise.getClass().getSimpleName() + ": " + value,
|
||||
Double.isFinite(value) && value >= 0D && value <= 1D);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ScalesNoiseTest {
|
||||
private static final double ZERO_SEED_PHASE_Z = 0.8833108082136426D;
|
||||
|
||||
@Test
|
||||
public void groovesTraceStaggeredSemicirclesAroundRaisedFaces() {
|
||||
ScalesNoise noise = new ScalesNoise(0L);
|
||||
for (int row = -3; row <= 3; row++) {
|
||||
double x = (row & 1) * 0.5D;
|
||||
double z = row * 0.5D - ZERO_SEED_PHASE_Z;
|
||||
assertTrue(noise.noise(x, z + 0.2D) > 0.75D);
|
||||
for (int point = 1; point < 12; point++) {
|
||||
double angle = point * Math.PI / 12D;
|
||||
assertTrue(noise.noise(x + Math.cos(angle) * 0.5D, z + Math.sin(angle) * 0.5D) < 0.24D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staggeredRowAndColumnBoundariesRemainContinuous() {
|
||||
ScalesNoise noise = new ScalesNoise(0L);
|
||||
for (int row = -4; row <= 4; row++) {
|
||||
double z = row * 0.5D - ZERO_SEED_PHASE_Z;
|
||||
for (int point = 0; point < 512; point++) {
|
||||
double x = point / 128D - 2D;
|
||||
assertEquals(noise.noise(x, z - 1.0E-8D), noise.noise(x, z + 1.0E-8D), 1.0E-5D);
|
||||
assertEquals(noise.noise(row * 0.5D - 1.0E-8D, x),
|
||||
noise.noise(row * 0.5D + 1.0E-8D, x), 1.0E-5D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.SplittableRandom;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class TextureNoiseTest {
|
||||
@Test
|
||||
public void texturesKeepUnitRangeAndExactHorizontalSlicesThroughSixteenOctaves() {
|
||||
SplittableRandom random = new SplittableRandom(715L);
|
||||
for (long seed : new long[]{0L, 1337L, Long.MIN_VALUE, Long.MAX_VALUE}) {
|
||||
for (PatternNoise noise : textures(seed)) {
|
||||
for (int octaves : new int[]{1, 4, 16}) {
|
||||
noise.setOctaves(octaves);
|
||||
for (int index = 0; index < 128; index++) {
|
||||
double x = random.nextDouble(-468750D, 468750D);
|
||||
double y = random.nextDouble(-32D, 32D);
|
||||
double z = random.nextDouble(-468750D, 468750D);
|
||||
double value = noise.noise(x, y, z);
|
||||
assertTrue(noise.getClass().getSimpleName(), Double.isFinite(value) && value >= 0D && value <= 1D);
|
||||
assertEquals(noise.noise(x, z), noise.noise(x, 0D, z), 0D);
|
||||
assertEquals(noise.noise(x), noise.noise(x, 0D, 0D), 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void texturesUseTheFullSeedAndEveryCoordinate() {
|
||||
PatternNoise[] first = textures(1337L);
|
||||
PatternNoise[] same = textures(1337L);
|
||||
PatternNoise[] highSeed = textures(1337L + (1L << 32));
|
||||
for (int style = 0; style < first.length; style++) {
|
||||
double seedDifference = 0D;
|
||||
double xDifference = 0D;
|
||||
double yDifference = 0D;
|
||||
double zDifference = 0D;
|
||||
for (int index = 0; index < 256; index++) {
|
||||
double x = index * 0.073D - 4D;
|
||||
double y = index * 0.011D - 1D;
|
||||
double z = index * -0.113D + 3D;
|
||||
double value = first[style].noise(x, y, z);
|
||||
assertEquals(value, same[style].noise(x, y, z), 0D);
|
||||
seedDifference += Math.abs(value - highSeed[style].noise(x, y, z));
|
||||
xDifference += Math.abs(value - first[style].noise(x + 0.31D, y, z));
|
||||
yDifference += Math.abs(value - first[style].noise(x, y + 0.37D, z));
|
||||
zDifference += Math.abs(value - first[style].noise(x, y, z + 0.41D));
|
||||
}
|
||||
assertTrue(first[style].getClass().getSimpleName(), seedDifference > 4D);
|
||||
assertTrue(first[style].getClass().getSimpleName(), xDifference > 4D);
|
||||
assertTrue(first[style].getClass().getSimpleName(), yDifference > 4D);
|
||||
assertTrue(first[style].getClass().getSimpleName(), zDifference > 4D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void octaveChangesAddDetailAndRestoreTheOriginalSamples() {
|
||||
for (PatternNoise noise : textures(701L)) {
|
||||
double[] original = new double[128];
|
||||
for (int index = 0; index < original.length; index++) {
|
||||
original[index] = noise.noise(index * 0.071D, 0.193D, index * -0.113D);
|
||||
}
|
||||
noise.setOctaves(4);
|
||||
double difference = 0D;
|
||||
for (int index = 0; index < original.length; index++) {
|
||||
difference += Math.abs(original[index] - noise.noise(index * 0.071D, 0.193D, index * -0.113D));
|
||||
}
|
||||
assertTrue(noise.getClass().getSimpleName(), difference > 1D);
|
||||
noise.setOctaves(0);
|
||||
for (int index = 0; index < original.length; index++) {
|
||||
assertEquals(original[index], noise.noise(index * 0.071D, 0.193D, index * -0.113D), 0D);
|
||||
}
|
||||
noise.setOctaves(16);
|
||||
double maximum = noise.noise(0.173D, 0.193D, -0.391D);
|
||||
noise.setOctaves(Integer.MAX_VALUE);
|
||||
assertEquals(maximum, noise.noise(0.173D, 0.193D, -0.391D), 0D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void textureSamplingHasNoIntegerCellSeams() {
|
||||
for (PatternNoise noise : textures(913L)) {
|
||||
for (int boundary = -4; boundary <= 4; boundary++) {
|
||||
for (int index = 0; index < 64; index++) {
|
||||
double first = index * 0.073D - 2D;
|
||||
double second = index * -0.113D + 3D;
|
||||
assertEquals(noise.noise(boundary - 1.0E-8D, first, second),
|
||||
noise.noise(boundary + 1.0E-8D, first, second), 1.0E-5D);
|
||||
assertEquals(noise.noise(first, boundary - 1.0E-8D, second),
|
||||
noise.noise(first, boundary + 1.0E-8D, second), 1.0E-5D);
|
||||
assertEquals(noise.noise(first, second, boundary - 1.0E-8D),
|
||||
noise.noise(first, second, boundary + 1.0E-8D), 1.0E-5D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static PatternNoise[] textures(long seed) {
|
||||
return new PatternNoise[]{new GaborNoise(seed), new MarbleNoise(seed), new ScalesNoise(seed)};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class TruchetNoiseTest {
|
||||
@Test
|
||||
public void quarterCircleRibbonsMeetAcrossTileBoundaries() {
|
||||
TruchetNoise xBoundaryNoise = new TruchetNoise(0L);
|
||||
TruchetNoise zBoundaryNoise = new TruchetNoise(-0x9E3779B97F4A7C15L);
|
||||
double epsilon = 1.0E-8D;
|
||||
double maximum = 0D;
|
||||
|
||||
for (int cell = -3; cell <= 3; cell++) {
|
||||
for (int index = 0; index <= 2048; index++) {
|
||||
double position = index / 512D - 2D;
|
||||
double x = cell - 0.17D;
|
||||
double z = cell - 0.13D;
|
||||
assertEquals(xBoundaryNoise.noise(x - epsilon, position),
|
||||
xBoundaryNoise.noise(x + epsilon, position), 1.0E-5D);
|
||||
assertEquals(zBoundaryNoise.noise(position, z - epsilon),
|
||||
zBoundaryNoise.noise(position, z + epsilon), 1.0E-5D);
|
||||
maximum = Math.max(maximum, xBoundaryNoise.noise(x, position));
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(maximum > 0.999D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void helicalContinuationIsContinuousThroughTheHorizontalSlice() {
|
||||
TruchetNoise noise = new TruchetNoise(713L);
|
||||
double heightVariation = 0D;
|
||||
double seedVariation = 0D;
|
||||
TruchetNoise otherSeed = new TruchetNoise(714L);
|
||||
|
||||
for (int index = 0; index < 256; index++) {
|
||||
double x = index * 0.037D - 4D;
|
||||
double z = index * -0.051D + 7D;
|
||||
double horizontal = noise.noise(x, z);
|
||||
assertEquals(horizontal, noise.noise(x, 0D, z), 0D);
|
||||
assertEquals(horizontal, noise.noise(x, -1.0E-8D, z), 1.0E-5D);
|
||||
assertEquals(horizontal, noise.noise(x, 1.0E-8D, z), 1.0E-5D);
|
||||
heightVariation += Math.abs(horizontal - noise.noise(x, 1.25D, z));
|
||||
seedVariation += Math.abs(noise.noise(x) - otherSeed.noise(x));
|
||||
}
|
||||
|
||||
assertTrue(heightVariation > 10D);
|
||||
assertTrue(seedVariation > 10D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void octavesStayBoundedAndRepeatableAtWorldBorders() {
|
||||
TruchetNoise first = new TruchetNoise(Long.MIN_VALUE);
|
||||
TruchetNoise second = new TruchetNoise(Long.MIN_VALUE);
|
||||
|
||||
for (int octaves : new int[]{1, 4, 16}) {
|
||||
first.setOctaves(octaves);
|
||||
second.setOctaves(octaves);
|
||||
for (double coordinate : new double[]{-468750D, -1D, -0D, 0.125D, 1D, 468750D}) {
|
||||
double value = first.noise(coordinate, coordinate * 0.37D, coordinate * -0.71D);
|
||||
assertTrue(Double.isFinite(value));
|
||||
assertTrue(value >= 0D && value <= 1D);
|
||||
assertEquals(value, second.noise(coordinate, coordinate * 0.37D, coordinate * -0.71D), 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class VortexNoiseTest {
|
||||
@Test
|
||||
public void spiralsStayContinuousAcrossCellBoundaries() {
|
||||
for (long seed : new long[]{0L, 42L, Long.MAX_VALUE}) {
|
||||
VortexNoise noise = new VortexNoise(seed);
|
||||
for (int cell = -8; cell <= 8; cell++) {
|
||||
for (int index = 0; index < 64; index++) {
|
||||
double coordinate = index * 0.0713D - 2D;
|
||||
assertEquals(noise.noise(cell - 1E-7D, 0.317D, coordinate),
|
||||
noise.noise(cell + 1E-7D, 0.317D, coordinate), 1E-5D);
|
||||
assertEquals(noise.noise(coordinate, 0.317D, cell - 1E-7D),
|
||||
noise.noise(coordinate, 0.317D, cell + 1E-7D), 1E-5D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void spiralCentersAndAngleWrapHaveNoJump() {
|
||||
VortexNoise noise = new VortexNoise(0L);
|
||||
double center = noise.noise(0.2D, 0.419D, 0.2D);
|
||||
for (int index = 0; index < 128; index++) {
|
||||
double angle = index * Math.PI / 64D;
|
||||
assertEquals(center, noise.noise(0.2D + Math.cos(angle) * 1E-8D, 0.419D,
|
||||
0.2D + Math.sin(angle) * 1E-8D), 1E-6D);
|
||||
double x = 0.2D - index * 0.007D;
|
||||
assertEquals(noise.noise(x, 0.419D, 0.2D - 1E-8D),
|
||||
noise.noise(x, 0.419D, 0.2D + 1E-8D), 1E-6D);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class WavePatternNoiseTest {
|
||||
private static final long SEED = 813753L;
|
||||
|
||||
@Test
|
||||
public void wavePatternsAreSeededBoundedAndContinuousInEveryAxis() {
|
||||
for (boolean gyroid : new boolean[]{true, false}) {
|
||||
NoiseGenerator first = gyroid ? new GyroidNoise(SEED) : new QuasicrystalNoise(SEED);
|
||||
NoiseGenerator repeated = gyroid ? new GyroidNoise(SEED) : new QuasicrystalNoise(SEED);
|
||||
NoiseGenerator otherSeed = gyroid ? new GyroidNoise(SEED + 1) : new QuasicrystalNoise(SEED + 1);
|
||||
double seedDifference = 0D;
|
||||
double horizontalDifference = 0D;
|
||||
double verticalDifference = 0D;
|
||||
double depthDifference = 0D;
|
||||
for (double origin : new double[]{-30_000_000D / 64D, 0D, 30_000_000D / 64D}) {
|
||||
for (int i = 0; i < 128; i++) {
|
||||
double x = origin + i * 0.137D;
|
||||
double y = i * 0.193D - 7D;
|
||||
double z = -origin + i * 0.317D;
|
||||
double value = first.noise(x, y, z);
|
||||
assertTrue(Double.isFinite(value) && value >= 0D && value <= 1D);
|
||||
assertEquals(value, repeated.noise(x, y, z), 0D);
|
||||
assertEquals(value, first.noise(x + 1E-6D, y, z), 1E-3D);
|
||||
assertEquals(value, first.noise(x, y + 1E-6D, z), 1E-3D);
|
||||
assertEquals(value, first.noise(x, y, z + 1E-6D), 1E-3D);
|
||||
seedDifference += Math.abs(value - otherSeed.noise(x, y, z));
|
||||
horizontalDifference += Math.abs(value - first.noise(x + 0.137D, y, z));
|
||||
verticalDifference += Math.abs(value - first.noise(x, y + 0.137D, z));
|
||||
depthDifference += Math.abs(value - first.noise(x, y, z + 0.137D));
|
||||
}
|
||||
}
|
||||
assertTrue(seedDifference > 10D);
|
||||
assertTrue(horizontalDifference > 10D);
|
||||
assertTrue(verticalDifference > 10D);
|
||||
assertTrue(depthDifference > 10D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void quasicrystalPreservesFivefoldRotationalSymmetryAcrossLayers() {
|
||||
QuasicrystalNoise noise = new QuasicrystalNoise(SEED);
|
||||
double angle = Math.PI * 2D / 5D;
|
||||
double cosine = Math.cos(angle);
|
||||
double sine = Math.sin(angle);
|
||||
for (int i = 0; i < 128; i++) {
|
||||
double x = i * 0.137D - 8D;
|
||||
double y = i * 0.031D;
|
||||
double z = i * 0.219D - 13D;
|
||||
double rotatedX = x * cosine - z * sine;
|
||||
double rotatedZ = x * sine + z * cosine;
|
||||
assertEquals(noise.noise(x, y, z), noise.noise(rotatedX, y, rotatedZ), 1E-12D);
|
||||
}
|
||||
assertNotEquals(noise.noise(0.19D, -0.37D), noise.noise(1.19D, -0.37D), 1E-4D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gyroidContainsBothOpenSpacesAndBrightSheets() {
|
||||
GyroidNoise noise = new GyroidNoise(SEED);
|
||||
int open = 0;
|
||||
int sheet = 0;
|
||||
int gradient = 0;
|
||||
for (int x = -64; x < 64; x++) {
|
||||
for (int z = -64; z < 64; z++) {
|
||||
double value = noise.noise(x * 0.127D, z * 0.127D);
|
||||
if (value < 0.05D) {
|
||||
open++;
|
||||
} else if (value > 0.8D) {
|
||||
sheet++;
|
||||
} else {
|
||||
gradient++;
|
||||
}
|
||||
}
|
||||
}
|
||||
assertTrue(open > 1638 && open < 13_107);
|
||||
assertTrue(sheet > 819);
|
||||
assertTrue(gradient > 1638);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class WhiteNoiseTest {
|
||||
@Test
|
||||
public void scatterDoesNotRepeatEvery8192Blocks() {
|
||||
WhiteNoise noise = new WhiteNoise(7153L);
|
||||
for (double x : new double[]{-29999999.25D, -8192D, 0D, 37.5D, 8192D}) {
|
||||
assertNotEquals(noise.noise(x, 73D), noise.noise(x + 8192D, 73D), 0D);
|
||||
assertNotEquals(noise.noise(x, 53D, 73D), noise.noise(x, 53D, 73D + 8192D), 0D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fractionalCoordinatesRemainDistinctNearWorldBorder() {
|
||||
WhiteNoise noise = new WhiteNoise(15273L);
|
||||
double x = 29999111.25D;
|
||||
assertNotEquals(noise.noise(x, -x), noise.noise(x + 0.0001D, -x), 0D);
|
||||
assertNotEquals(noise.noise(x, 71.5D, -x), noise.noise(x, 71.5001D, -x), 0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scatterIsRepeatableAndUsesFullSeed() {
|
||||
WhiteNoise first = new WhiteNoise(725L);
|
||||
WhiteNoise same = new WhiteNoise(725L);
|
||||
WhiteNoise upperBits = new WhiteNoise(725L + (1L << 48));
|
||||
for (int index = 0; index < 512; index++) {
|
||||
double x = index * 1.25D;
|
||||
double value = first.noise(x, 53D, -x);
|
||||
assertEquals(value, same.noise(x, 53D, -x), 0D);
|
||||
assertTrue(value >= 0D && value <= 1D);
|
||||
assertNotEquals(value, upperBits.noise(x, 53D, -x), 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user