mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-27 14:30:14 +00:00
Reused nested fracture samples
- Pure deep fracture chains reuse coordinate pairs within each sample - Each sample rechecks runtime noise purity - Tests cover bit parity, sample counts, mutations and concurrent engines
This commit is contained in:
1 parent
12591820e8
commit
fac3b4e106
2 files changed
+409
-11
No files matched your search
@@ -31,6 +31,14 @@ import art.arcane.volmlib.util.collection.KList;
|
|||||||
import art.arcane.volmlib.util.math.RNG;
|
import art.arcane.volmlib.util.math.RNG;
|
||||||
import art.arcane.volmlib.util.interpolation.IrisInterpolation;
|
import art.arcane.volmlib.util.interpolation.IrisInterpolation;
|
||||||
import art.arcane.volmlib.util.noise.CNG;
|
import art.arcane.volmlib.util.noise.CNG;
|
||||||
|
import art.arcane.volmlib.util.noise.FlatNoise;
|
||||||
|
import art.arcane.volmlib.util.noise.FractalBillowPerlinNoise;
|
||||||
|
import art.arcane.volmlib.util.noise.FractalBillowSimplexNoise;
|
||||||
|
import art.arcane.volmlib.util.noise.FractalFBMSimplexNoise;
|
||||||
|
import art.arcane.volmlib.util.noise.NoiseGenerator;
|
||||||
|
import art.arcane.volmlib.util.noise.OffsetNoiseGenerator;
|
||||||
|
import art.arcane.volmlib.util.noise.PerlinNoise;
|
||||||
|
import art.arcane.volmlib.util.noise.SimplexNoise;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -50,6 +58,9 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
public class IrisNoiseGenerator {
|
public class IrisNoiseGenerator {
|
||||||
private static final int GENERATOR_CACHE_SIZE = 32;
|
private static final int GENERATOR_CACHE_SIZE = 32;
|
||||||
private static final long GENERATOR_SEED_SALT = 33_955_677L;
|
private static final long GENERATOR_SEED_SALT = 33_955_677L;
|
||||||
|
private static final int MINIMUM_FRACTURE_DEPTH = 4;
|
||||||
|
private static final int MAXIMUM_FRACTURE_NODES = 32;
|
||||||
|
private static final ThreadLocal<FractureSamples> FRACTURE_SAMPLES = ThreadLocal.withInitial(FractureSamples::new);
|
||||||
|
|
||||||
@Getter(AccessLevel.NONE)
|
@Getter(AccessLevel.NONE)
|
||||||
@Setter(AccessLevel.NONE)
|
@Setter(AccessLevel.NONE)
|
||||||
@@ -135,6 +146,20 @@ public class IrisNoiseGenerator {
|
|||||||
return offsetY;
|
return offsetY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasDeepFracture()) {
|
||||||
|
FractureSamples samples = FRACTURE_SAMPLES.get();
|
||||||
|
if (!samples.active) {
|
||||||
|
samples.active = true;
|
||||||
|
try {
|
||||||
|
if (samples.prepare(this, superSeed, data, engine)) {
|
||||||
|
return samples.sample(xv, zv);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
samples.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
double x = xv;
|
double x = xv;
|
||||||
double z = zv;
|
double z = zv;
|
||||||
int g = 33;
|
int g = 33;
|
||||||
@@ -148,17 +173,7 @@ public class IrisNoiseGenerator {
|
|||||||
g += 819;
|
g += 819;
|
||||||
}
|
}
|
||||||
|
|
||||||
CNG cng = getGenerator(superSeed, data, engine);
|
return sample(getGenerator(superSeed, data, engine), x, z);
|
||||||
double sampleX = (x / zoom) + offsetX;
|
|
||||||
double sampleZ = (z / zoom) + offsetZ;
|
|
||||||
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, 2D) : n;
|
|
||||||
n = bezier ? IrisInterpolation.bezier(n) : n;
|
|
||||||
n = sinCentered ? IrisInterpolation.sinCenter(n) : n;
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public KList<IrisNoiseGenerator> getAllComposites() {
|
public KList<IrisNoiseGenerator> getAllComposites() {
|
||||||
@@ -173,6 +188,65 @@ public class IrisNoiseGenerator {
|
|||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private double sample(CNG cng, double x, double z) {
|
||||||
|
double sampleX = (x / zoom) + offsetX;
|
||||||
|
double sampleZ = (z / zoom) + offsetZ;
|
||||||
|
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, 2D) : n;
|
||||||
|
n = bezier ? IrisInterpolation.bezier(n) : n;
|
||||||
|
n = sinCentered ? IrisInterpolation.sinCenter(n) : n;
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasDeepFracture() {
|
||||||
|
IrisNoiseGenerator node = this;
|
||||||
|
for (int depth = 0; depth < MINIMUM_FRACTURE_DEPTH; depth++) {
|
||||||
|
if (node.getClass() != IrisNoiseGenerator.class || !node.enabled || node.fracture.size() != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
node = node.fracture.get(0);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasPureStyle() {
|
||||||
|
return style != null && style.getClass() == IrisGeneratorStyle.class
|
||||||
|
&& style.getExpression() == null && style.getImageMap() == null
|
||||||
|
&& style.getFracture() == null && style.getCellularFrequency() == 0D
|
||||||
|
&& style.getCacheSize() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isPureGenerator(CNG cng) {
|
||||||
|
for (int depth = 0; depth < MAXIMUM_FRACTURE_NODES; depth++) {
|
||||||
|
if (cng.getClass() != CNG.class || cng.getChildren() != null
|
||||||
|
|| cng.getCustomGenerator() != null || cng.getInjector() != CNG.ADD
|
||||||
|
|| !isPureBase(cng.getGenerator())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cng = cng.getFracture();
|
||||||
|
if (cng == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isPureBase(NoiseGenerator generator) {
|
||||||
|
for (int depth = 0; depth < MAXIMUM_FRACTURE_NODES && generator != null; depth++) {
|
||||||
|
Class<?> type = generator.getClass();
|
||||||
|
if (type != OffsetNoiseGenerator.class) {
|
||||||
|
return type == SimplexNoise.class || type == PerlinNoise.class || type == FlatNoise.class
|
||||||
|
|| type == FractalBillowPerlinNoise.class || type == FractalBillowSimplexNoise.class
|
||||||
|
|| type == FractalFBMSimplexNoise.class;
|
||||||
|
}
|
||||||
|
generator = ((OffsetNoiseGenerator) generator).getBase();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static final class GeneratorKey {
|
private static final class GeneratorKey {
|
||||||
private final IrisData data;
|
private final IrisData data;
|
||||||
private final Engine engine;
|
private final Engine engine;
|
||||||
@@ -208,6 +282,59 @@ public class IrisNoiseGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class FractureSamples {
|
||||||
|
private final IrisNoiseGenerator[] nodes = new IrisNoiseGenerator[MAXIMUM_FRACTURE_NODES];
|
||||||
|
private final CNG[] generators = new CNG[MAXIMUM_FRACTURE_NODES];
|
||||||
|
private int size;
|
||||||
|
private boolean active;
|
||||||
|
|
||||||
|
private boolean prepare(IrisNoiseGenerator node, long superSeed, IrisData data, Engine engine) {
|
||||||
|
while (size < MAXIMUM_FRACTURE_NODES) {
|
||||||
|
if (node.getClass() != IrisNoiseGenerator.class || !node.enabled || !node.hasPureStyle()
|
||||||
|
|| node.fracture.size() > 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CNG generator = node.getGenerator(superSeed, data, engine);
|
||||||
|
if (!isPureGenerator(generator)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
nodes[size] = node;
|
||||||
|
generators[size++] = generator;
|
||||||
|
if (node.fracture.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
superSeed += node.seed + 33;
|
||||||
|
node = node.fracture.get(0);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double sample(double x, double z) {
|
||||||
|
int leaf = size - 1;
|
||||||
|
double forward = nodes[leaf].sample(generators[leaf], x, z);
|
||||||
|
double reverse = nodes[leaf].sample(generators[leaf], z, x);
|
||||||
|
for (int index = leaf - 1; index >= 0; index--) {
|
||||||
|
double offset = nodes[index + 1].opacity / 2D;
|
||||||
|
double forwardX = x + (forward - offset);
|
||||||
|
double forwardZ = z + (reverse - offset);
|
||||||
|
forward = nodes[index].sample(generators[index], forwardX, forwardZ);
|
||||||
|
if (index > 0) {
|
||||||
|
reverse = nodes[index].sample(generators[index], forwardZ, forwardX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return forward;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clear() {
|
||||||
|
for (int index = 0; index < size; index++) {
|
||||||
|
nodes[index] = null;
|
||||||
|
generators[index] = null;
|
||||||
|
}
|
||||||
|
size = 0;
|
||||||
|
active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final class CachedGenerator {
|
private static final class CachedGenerator {
|
||||||
private final GeneratorKey key;
|
private final GeneratorKey key;
|
||||||
private final CNG generator;
|
private final CNG generator;
|
||||||
|
|||||||
+271
@@ -0,0 +1,271 @@
|
|||||||
|
package art.arcane.iris.generation.noise;
|
||||||
|
|
||||||
|
import art.arcane.iris.generation.runtime.Engine;
|
||||||
|
import art.arcane.iris.pack.loading.IrisData;
|
||||||
|
import art.arcane.volmlib.util.collection.KList;
|
||||||
|
import art.arcane.volmlib.util.interpolation.IrisInterpolation;
|
||||||
|
import art.arcane.volmlib.util.noise.CNG;
|
||||||
|
import art.arcane.volmlib.util.noise.NoiseGenerator;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
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;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyDouble;
|
||||||
|
import static org.mockito.Mockito.clearInvocations;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
public final class IrisNoiseGeneratorFractureTest {
|
||||||
|
@Test
|
||||||
|
public void deepChainsMatchRecursiveSamplingBits() {
|
||||||
|
Random coordinates = new Random(718311L);
|
||||||
|
for (NoiseStyle style : List.of(NoiseStyle.SIMPLEX, NoiseStyle.PERLIN, NoiseStyle.IRIS,
|
||||||
|
NoiseStyle.FRACTAL_BILLOW_SIMPLEX, NoiseStyle.FRACTAL_FBM_SIMPLEX)) {
|
||||||
|
for (int depth : new int[]{0, 1, 3, 4, 5, 8}) {
|
||||||
|
IrisNoiseGenerator root = chain(depth, style).getFirst();
|
||||||
|
for (long seed : new long[]{0L, -4193L, Long.MIN_VALUE, Long.MAX_VALUE}) {
|
||||||
|
assertParity(root, seed, 0D, -0D, null, null);
|
||||||
|
assertParity(root, seed, -0D, 0D, null, null);
|
||||||
|
assertParity(root, seed, -30_000_000D, 29_999_999.75D, null, null);
|
||||||
|
assertParity(root, seed, Math.nextDown(-64D), Math.nextUp(64D), null, null);
|
||||||
|
for (int index = 0; index < 8; index++) {
|
||||||
|
assertParity(root, seed, coordinates.nextDouble() * 4096D - 2048D,
|
||||||
|
coordinates.nextDouble() * 4096D - 2048D, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fourFracturesReducePrimitiveSamplesFromThirtyOneToNine() {
|
||||||
|
List<IrisNoiseGenerator> chain = chain(4, NoiseStyle.SIMPLEX);
|
||||||
|
List<NoiseGenerator> bases = spyBases(chain, 71L);
|
||||||
|
reference(chain.getFirst(), 71L, 13.25D, -21.75D, null, null);
|
||||||
|
for (int index = 0; index < bases.size(); index++) {
|
||||||
|
verify(bases.get(index), times(1 << index)).noise(anyDouble(), anyDouble());
|
||||||
|
clearInvocations(bases.get(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
chain.getFirst().getNoise(71L, 13.25D, -21.75D, null);
|
||||||
|
for (int index = 0; index < bases.size(); index++) {
|
||||||
|
verify(bases.get(index), times(index == 0 ? 1 : 2)).noise(anyDouble(), anyDouble());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shallowChainsKeepTheirSamplingCounts() {
|
||||||
|
List<IrisNoiseGenerator> chain = chain(3, NoiseStyle.SIMPLEX);
|
||||||
|
List<NoiseGenerator> bases = spyBases(chain, 71L);
|
||||||
|
chain.getFirst().getNoise(71L, 13.25D, -21.75D, null);
|
||||||
|
for (int index = 0; index < bases.size(); index++) {
|
||||||
|
verify(bases.get(index), times(1 << index)).noise(anyDouble(), anyDouble());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void changedParametersAndFracturesRemainVisible() {
|
||||||
|
List<IrisNoiseGenerator> chain = chain(5, NoiseStyle.IRIS);
|
||||||
|
IrisNoiseGenerator root = chain.getFirst();
|
||||||
|
assertParity(root, 19L, 31D, -79D, null, null);
|
||||||
|
chain.get(3).setOffsetX(-319D).setOffsetZ(17D).setOffsetY(-0.7D)
|
||||||
|
.setSeed(Long.MAX_VALUE).setOpacity(0.35D).setZoom(17D);
|
||||||
|
assertParity(root, 19L, 31D, -79D, null, null);
|
||||||
|
chain.get(4).setEnabled(false);
|
||||||
|
assertParity(root, 19L, 31D, -79D, null, null);
|
||||||
|
chain.get(4).setEnabled(true);
|
||||||
|
chain.get(2).getFracture().add(new IrisNoiseGenerator().setStyle(NoiseStyle.PERLIN.style()));
|
||||||
|
assertParity(root, 19L, 31D, -79D, null, null);
|
||||||
|
chain.get(2).getFracture().removeLast();
|
||||||
|
chain.getLast().getFracture().add(new IrisNoiseGenerator().setStyle(NoiseStyle.SIMPLEX.style()));
|
||||||
|
assertParity(root, 19L, 31D, -79D, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void statefulRuntimeGeneratorsKeepAllEvaluations() {
|
||||||
|
List<IrisNoiseGenerator> chain = chain(4, NoiseStyle.SIMPLEX);
|
||||||
|
long leafSeed = childSeed(chain, 23L, chain.size() - 1);
|
||||||
|
StatefulNoise stateful = new StatefulNoise();
|
||||||
|
chain.getLast().getGenerator(leafSeed, null, null).setGenerator(stateful);
|
||||||
|
double expected = reference(chain.getFirst(), 23L, 17D, -31D, null, null);
|
||||||
|
assertEquals(16, stateful.samples);
|
||||||
|
stateful.samples = 0;
|
||||||
|
double actual = chain.getFirst().getNoise(23L, 17D, -31D, null);
|
||||||
|
assertBits(expected, actual);
|
||||||
|
assertEquals(16, stateful.samples);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void expressionConfigurationDoesNotReuseEvenPreviouslyCachedPureNoise() {
|
||||||
|
List<IrisNoiseGenerator> chain = chain(4, NoiseStyle.SIMPLEX);
|
||||||
|
List<NoiseGenerator> bases = spyBases(chain, 71L);
|
||||||
|
chain.getLast().getStyle().setExpression("custom");
|
||||||
|
chain.getFirst().getNoise(71L, 13.25D, -21.75D, null);
|
||||||
|
verify(bases.getLast(), times(16)).noise(anyDouble(), anyDouble());
|
||||||
|
chain.getLast().getStyle().setExpression(null).setImageMap("map");
|
||||||
|
clearInvocations(bases.getLast());
|
||||||
|
chain.getFirst().getNoise(71L, 13.25D, -21.75D, null);
|
||||||
|
verify(bases.getLast(), times(16)).noise(anyDouble(), anyDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void customStylesKeepTheScalarPath() {
|
||||||
|
List<IrisNoiseGenerator> chain = chain(4, NoiseStyle.SIMPLEX);
|
||||||
|
List<NoiseGenerator> bases = spyBases(chain, 71L);
|
||||||
|
chain.getLast().setStyle(new IrisGeneratorStyle(NoiseStyle.SIMPLEX) {
|
||||||
|
});
|
||||||
|
chain.getFirst().getNoise(71L, 13.25D, -21.75D, null);
|
||||||
|
verify(bases.getLast(), times(16)).noise(anyDouble(), anyDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failedSamplingReleasesTheThreadScratch() {
|
||||||
|
List<IrisNoiseGenerator> chain = chain(4, NoiseStyle.SIMPLEX);
|
||||||
|
List<NoiseGenerator> bases = spyBases(chain, 71L);
|
||||||
|
doThrow(new IllegalStateException("sample failed")).doCallRealMethod()
|
||||||
|
.when(bases.getLast()).noise(anyDouble(), anyDouble());
|
||||||
|
assertThrows(IllegalStateException.class,
|
||||||
|
() -> chain.getFirst().getNoise(71L, 13.25D, -21.75D, null));
|
||||||
|
clearInvocations(bases.getLast());
|
||||||
|
chain.getFirst().getNoise(71L, 13.25D, -21.75D, null);
|
||||||
|
verify(bases.getLast(), times(2)).noise(anyDouble(), anyDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void threadsAndEngineIdentitiesRemainIndependent() throws Exception {
|
||||||
|
IrisNoiseGenerator root = chain(5, NoiseStyle.IRIS).getFirst();
|
||||||
|
IrisData data = mock(IrisData.class);
|
||||||
|
Engine firstEngine = mock(Engine.class);
|
||||||
|
Engine secondEngine = mock(Engine.class);
|
||||||
|
CountDownLatch start = new CountDownLatch(1);
|
||||||
|
ExecutorService executor = Executors.newFixedThreadPool(2);
|
||||||
|
try {
|
||||||
|
Future<?> first = executor.submit(() -> sampleConcurrently(root, data, firstEngine, 47L, start));
|
||||||
|
Future<?> second = executor.submit(() -> sampleConcurrently(root, data, secondEngine, -131L, start));
|
||||||
|
start.countDown();
|
||||||
|
first.get(15L, TimeUnit.SECONDS);
|
||||||
|
second.get(15L, TimeUnit.SECONDS);
|
||||||
|
} finally {
|
||||||
|
executor.shutdownNow();
|
||||||
|
}
|
||||||
|
assertParity(root, 47L, 17D, 19D, data, secondEngine);
|
||||||
|
assertParity(root, 47L, 17D, 19D, data, firstEngine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void sampleConcurrently(IrisNoiseGenerator root, IrisData data, Engine engine,
|
||||||
|
long seed, CountDownLatch start) {
|
||||||
|
try {
|
||||||
|
assertTrue(start.await(5L, TimeUnit.SECONDS));
|
||||||
|
} catch (InterruptedException failure) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new AssertionError(failure);
|
||||||
|
}
|
||||||
|
for (int index = 0; index < 64; index++) {
|
||||||
|
assertParity(root, seed, index * 13.75D, -index * 2.25D, data, engine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<IrisNoiseGenerator> chain(int depth, NoiseStyle style) {
|
||||||
|
List<IrisNoiseGenerator> nodes = new ArrayList<>(depth + 1);
|
||||||
|
for (int index = 0; index <= depth; index++) {
|
||||||
|
IrisNoiseGenerator node = new IrisNoiseGenerator().setStyle(style.style())
|
||||||
|
.setSeed(index * 713L).setZoom(0.75D + index).setOpacity(0.63D + index * 0.01D)
|
||||||
|
.setOffsetX(index * -0.2D).setOffsetY(index * -0.03D).setOffsetZ(index * 0.4D)
|
||||||
|
.setNegative(index % 2 == 0).setExponent(index % 3 == 0 ? 1.25D : 1D)
|
||||||
|
.setParametric(index % 3 == 1).setBezier(index % 3 == 2)
|
||||||
|
.setSinCentered(index % 4 == 1);
|
||||||
|
if (!nodes.isEmpty()) {
|
||||||
|
nodes.getLast().setFracture(new KList<>(node));
|
||||||
|
}
|
||||||
|
nodes.add(node);
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<NoiseGenerator> spyBases(List<IrisNoiseGenerator> nodes, long superSeed) {
|
||||||
|
List<NoiseGenerator> bases = new ArrayList<>(nodes.size());
|
||||||
|
for (IrisNoiseGenerator node : nodes) {
|
||||||
|
CNG generator = node.getGenerator(superSeed, null, null);
|
||||||
|
NoiseGenerator base = spy(generator.getGenerator());
|
||||||
|
generator.setGenerator(base);
|
||||||
|
bases.add(base);
|
||||||
|
superSeed += node.getSeed() + 33;
|
||||||
|
}
|
||||||
|
return bases;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long childSeed(List<IrisNoiseGenerator> nodes, long seed, int index) {
|
||||||
|
for (int child = 0; child < index; child++) {
|
||||||
|
seed += nodes.get(child).getSeed() + 33;
|
||||||
|
}
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertParity(IrisNoiseGenerator node, long seed, double x, double z,
|
||||||
|
IrisData data, Engine engine) {
|
||||||
|
assertBits(reference(node, seed, x, z, data, engine), node.getNoise(seed, x, z, data, engine));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertBits(double expected, double actual) {
|
||||||
|
assertEquals(Double.doubleToRawLongBits(expected), Double.doubleToRawLongBits(actual));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double reference(IrisNoiseGenerator node, long superSeed, double xv, double zv,
|
||||||
|
IrisData data, Engine engine) {
|
||||||
|
if (!node.isEnabled()) {
|
||||||
|
return node.getOffsetY();
|
||||||
|
}
|
||||||
|
double x = xv;
|
||||||
|
double z = zv;
|
||||||
|
int salt = 33;
|
||||||
|
for (IrisNoiseGenerator child : node.getFracture()) {
|
||||||
|
if (child.isEnabled()) {
|
||||||
|
double offset = child.getOpacity() / 2D;
|
||||||
|
x += reference(child, superSeed + node.getSeed() + salt, xv, zv, data, engine) - offset;
|
||||||
|
z += reference(child, superSeed + node.getSeed() + salt, zv, xv, data, engine) - offset;
|
||||||
|
}
|
||||||
|
salt += 819;
|
||||||
|
}
|
||||||
|
double sampleX = (x / node.getZoom()) + node.getOffsetX();
|
||||||
|
double sampleZ = (z / node.getZoom()) + node.getOffsetZ();
|
||||||
|
double result = node.getGenerator(superSeed, data, engine).noiseFast2D(sampleX, sampleZ) * node.getOpacity();
|
||||||
|
result = node.isNegative() ? -result + node.getOpacity() : result;
|
||||||
|
result = (node.getExponent() != 1D ? result < 0D ? -Math.pow(-result, node.getExponent())
|
||||||
|
: Math.pow(result, node.getExponent()) : result) + node.getOffsetY();
|
||||||
|
result = node.isParametric() ? IrisInterpolation.parametric(result, 2D) : result;
|
||||||
|
result = node.isBezier() ? IrisInterpolation.bezier(result) : result;
|
||||||
|
return node.isSinCentered() ? IrisInterpolation.sinCenter(result) : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class StatefulNoise implements NoiseGenerator {
|
||||||
|
private int samples;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double noise(double x) {
|
||||||
|
return ++samples * 0.01D;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double noise(double x, double z) {
|
||||||
|
return ++samples * 0.01D;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double noise(double x, double y, double z) {
|
||||||
|
return ++samples * 0.01D;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user