mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-12 03:24:14 +00:00
Isolate scaled noise generators
- Isolate biome-child and carving-child noise generators. - Isolate decorator variance generators. - Share fresh scaled-generator construction. - Test shared-generator isolation at each caller.
This commit is contained in:
@@ -93,6 +93,7 @@ import java.util.Objects;
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class IrisBiome extends IrisRegistrant implements Rarity {
|
||||
private static final int BIOME_GENERATOR_CACHE_SIZE = 8;
|
||||
private static final int CHILDREN_SEED_SALT = 2137;
|
||||
|
||||
private final transient AtomicCache<KMap<String, IrisBiomeGeneratorLink>> genCache = new AtomicCache<>();
|
||||
private final transient AtomicCache<KMap<String, Integer>> genCacheMax = new AtomicCache<>();
|
||||
@@ -511,7 +512,15 @@ public class IrisBiome extends IrisRegistrant implements Rarity {
|
||||
}
|
||||
|
||||
public CNG getChildrenGenerator(RNG random, int sig, double scale) {
|
||||
return childrenCell.aquire(() -> getChildStyle().create(random.nextParallelRNG(sig * 2137), getLoader()).bake().scale(scale).bake());
|
||||
return childrenCell.aquire(() -> createChildrenGenerator(random, sig, scale));
|
||||
}
|
||||
|
||||
private CNG createChildrenGenerator(RNG random, int sig, double scale) {
|
||||
return getChildStyle().createScaledGenerator(deriveChildStyleSeed(random, sig), getLoader(), scale);
|
||||
}
|
||||
|
||||
static RNG deriveChildStyleSeed(RNG random, int signature) {
|
||||
return random.nextParallelRNG(signature * CHILDREN_SEED_SALT);
|
||||
}
|
||||
|
||||
public KList<NativeBlockState> generateLayers(IrisDimension dim, double wx, double wz, RNG random, int maxDepth, int height, IrisData rdata, IrisComplex complex) {
|
||||
|
||||
@@ -151,15 +151,19 @@ public class IrisDecorator {
|
||||
|
||||
public CNG getVarianceGenerator(RNG rng, IrisData data) {
|
||||
CNG cached = varianceGenerator.getIfPresent();
|
||||
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
return cached != null ? cached : buildVarianceGeneratorOnce(rng, data);
|
||||
}
|
||||
|
||||
return varianceGenerator.aquire(() ->
|
||||
variance.create(
|
||||
rng.nextParallelRNG(getBlockData(data).size()), data)
|
||||
.scale(1D / variance.getZoom()));
|
||||
private CNG buildVarianceGeneratorOnce(RNG rng, IrisData data) {
|
||||
return varianceGenerator.aquire(() -> createVarianceGenerator(rng, data));
|
||||
}
|
||||
|
||||
private CNG createVarianceGenerator(RNG rng, IrisData data) {
|
||||
return variance.createNoCache(deriveVarianceSeed(rng, data), data).scale(1D / variance.getZoom());
|
||||
}
|
||||
|
||||
RNG deriveVarianceSeed(RNG rng, IrisData data) {
|
||||
return rng.nextParallelRNG(getBlockData(data).size());
|
||||
}
|
||||
|
||||
public KList<IrisBlockData> add(String b) {
|
||||
|
||||
@@ -102,6 +102,10 @@ public class IrisGeneratorStyle {
|
||||
return createNoCache(rng, data, 1, 0, false, resolveEngine(data));
|
||||
}
|
||||
|
||||
public CNG createScaledGenerator(RNG rng, IrisData data, double scaleFactor) {
|
||||
return createNoCache(rng, data).bake().scale(scaleFactor).bake();
|
||||
}
|
||||
|
||||
public CNG createForLayer(RNG rng, IrisData data, int octaveMultiplier) {
|
||||
return createNoCache(rng, data, Math.clamp(octaveMultiplier, 1, 16), 0, false, resolveEngine(data));
|
||||
}
|
||||
|
||||
@@ -11,18 +11,18 @@ public final class ContinentGenerator {
|
||||
}
|
||||
|
||||
public static CNG create(IrisDimension dimension, RNG parentSeedSource, IrisData packData) {
|
||||
return scaleByContinentZoom(createContinentalStyleGenerator(dimension, parentSeedSource, packData), dimension);
|
||||
return createScaledContinentalStyleGenerator(dimension, deriveContinentSeed(parentSeedSource), packData);
|
||||
}
|
||||
|
||||
private static CNG createContinentalStyleGenerator(IrisDimension dimension, RNG parentSeedSource, IrisData packData) {
|
||||
return dimension.getContinentalStyle().createNoCache(deriveContinentSeed(parentSeedSource), packData);
|
||||
private static CNG createScaledContinentalStyleGenerator(IrisDimension dimension, RNG continentSeed, IrisData packData) {
|
||||
return dimension.getContinentalStyle().createScaledGenerator(continentSeed, packData, inverseContinentZoom(dimension));
|
||||
}
|
||||
|
||||
static RNG deriveContinentSeed(RNG parentSeedSource) {
|
||||
return parentSeedSource.nextParallelRNG(CONTINENT_SEED_SALT);
|
||||
}
|
||||
|
||||
private static CNG scaleByContinentZoom(CNG generator, IrisDimension dimension) {
|
||||
return generator.bake().scale(1D / dimension.getContinentZoom()).bake();
|
||||
private static double inverseContinentZoom(IrisDimension dimension) {
|
||||
return 1D / dimension.getContinentZoom();
|
||||
}
|
||||
}
|
||||
|
||||
+23
-7
@@ -31,6 +31,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
@Description("Dimension-level cave biome override with absolute world Y bounds.")
|
||||
@Data
|
||||
public class IrisDimensionCarvingEntry {
|
||||
private static final int CHILDREN_SEED_SALT = 2137;
|
||||
private static final double MINIMUM_CHILD_SHRINK_FACTOR = 0.0001D;
|
||||
private final transient AtomicCache<IrisBiome> realBiome = new AtomicCache<>(true);
|
||||
private final transient Map<Long, CNG> childGenerators = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -81,13 +83,27 @@ public class IrisDimensionCarvingEntry {
|
||||
}
|
||||
|
||||
public CNG getChildrenGenerator(long seed, IrisData data) {
|
||||
return childGenerators.computeIfAbsent(deriveChildrenGeneratorSeed(seed), key -> createChildrenGenerator(key, data));
|
||||
}
|
||||
|
||||
private CNG createChildrenGenerator(long childrenGeneratorSeed, IrisData data) {
|
||||
return getChildStyle().createScaledGenerator(deriveChildStyleSeed(childrenGeneratorSeed), data, childShrinkFactor());
|
||||
}
|
||||
|
||||
long deriveChildrenGeneratorSeed(long worldSeed) {
|
||||
return worldSeed ^ (hashOfId() << 32) ^ CHILDREN_SEED_SALT;
|
||||
}
|
||||
|
||||
static RNG deriveChildStyleSeed(long childrenGeneratorSeed) {
|
||||
return new RNG(childrenGeneratorSeed).nextParallelRNG(CHILDREN_SEED_SALT);
|
||||
}
|
||||
|
||||
private double childShrinkFactor() {
|
||||
return Math.max(MINIMUM_CHILD_SHRINK_FACTOR, getChildShrinkFactor());
|
||||
}
|
||||
|
||||
private long hashOfId() {
|
||||
String entryId = getId();
|
||||
long idHash = entryId == null ? 0L : entryId.trim().hashCode();
|
||||
long generatorSeed = seed ^ (idHash << 32) ^ 2137L;
|
||||
return childGenerators.computeIfAbsent(generatorSeed, key -> {
|
||||
double scale = Math.max(0.0001D, getChildShrinkFactor());
|
||||
RNG random = new RNG(key);
|
||||
return getChildStyle().create(random.nextParallelRNG(2137), data).bake().scale(scale).bake();
|
||||
});
|
||||
return entryId == null ? 0L : entryId.trim().hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package art.arcane.iris.generation.biome;
|
||||
|
||||
import art.arcane.iris.generation.noise.IrisGeneratorStyle;
|
||||
import art.arcane.iris.generation.noise.NoiseStyle;
|
||||
import art.arcane.iris.pack.loading.IrisData;
|
||||
import art.arcane.iris.testsupport.RunningEnginePackData;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.junit.Test;
|
||||
|
||||
import static art.arcane.iris.testsupport.GeneratorSampling.SAMPLE_BLOCKS;
|
||||
import static art.arcane.iris.testsupport.GeneratorSampling.sampleSharedGenerator;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
public class IrisBiomeChildrenGeneratorTest {
|
||||
private static final long WORLD_SEED = 7L;
|
||||
private static final int CHILDREN_SIGNATURE = 123;
|
||||
private static final double CHILD_SHRINK_FACTOR = 4D;
|
||||
|
||||
private final IrisData packData = RunningEnginePackData.create();
|
||||
private final IrisBiome biome = createBiomeWithSimplexChildStyle(packData);
|
||||
|
||||
@Test
|
||||
public void doesBuildingTheChildrenGeneratorLeaveTheSharedChildStyleGeneratorUnscaled() {
|
||||
assertArrayEquals(sampleUntouchedSharedChildStyleGenerator(), sampleSharedChildStyleGeneratorAfterChildrenGeneratorBuilt(), 0D);
|
||||
}
|
||||
|
||||
private double[] sampleSharedChildStyleGeneratorAfterChildrenGeneratorBuilt() {
|
||||
buildChildrenGenerator();
|
||||
return sampleSharedChildStyleGenerator(biome.getChildStyle());
|
||||
}
|
||||
|
||||
private void buildChildrenGenerator() {
|
||||
biome.getChildrenGenerator(new RNG(WORLD_SEED), CHILDREN_SIGNATURE, CHILD_SHRINK_FACTOR);
|
||||
}
|
||||
|
||||
private double[] sampleUntouchedSharedChildStyleGenerator() {
|
||||
return sampleSharedChildStyleGenerator(createSimplexStyle());
|
||||
}
|
||||
|
||||
private double[] sampleSharedChildStyleGenerator(IrisGeneratorStyle childStyle) {
|
||||
return sampleSharedGenerator(childStyle, deriveSharedChildStyleSeed(), packData);
|
||||
}
|
||||
|
||||
private static RNG deriveSharedChildStyleSeed() {
|
||||
return IrisBiome.deriveChildStyleSeed(new RNG(WORLD_SEED), CHILDREN_SIGNATURE);
|
||||
}
|
||||
|
||||
private static IrisBiome createBiomeWithSimplexChildStyle(IrisData packData) {
|
||||
IrisBiome biome = new IrisBiome().setChildStyle(createSimplexStyle());
|
||||
biome.setLoader(packData);
|
||||
return biome;
|
||||
}
|
||||
|
||||
private static IrisGeneratorStyle createSimplexStyle() {
|
||||
return new IrisGeneratorStyle(NoiseStyle.SIMPLEX);
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package art.arcane.iris.generation.decoration;
|
||||
|
||||
import art.arcane.iris.generation.block.IrisBlockData;
|
||||
import art.arcane.iris.generation.noise.IrisGeneratorStyle;
|
||||
import art.arcane.iris.generation.noise.NoiseStyle;
|
||||
import art.arcane.iris.pack.loading.IrisData;
|
||||
import art.arcane.iris.testsupport.RunningEnginePackData;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.junit.Test;
|
||||
|
||||
import static art.arcane.iris.testsupport.GeneratorSampling.SAMPLE_BLOCKS;
|
||||
import static art.arcane.iris.testsupport.GeneratorSampling.sampleSharedGenerator;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
public class IrisDecoratorVarianceGeneratorTest {
|
||||
private static final long WORLD_SEED = 7L;
|
||||
private static final double VARIANCE_ZOOM = 4D;
|
||||
|
||||
private final IrisData packData = RunningEnginePackData.create();
|
||||
private final IrisDecorator decorator = createDecoratorWithZoomedSimplexVariance();
|
||||
|
||||
@Test
|
||||
public void doesBuildingTheVarianceGeneratorLeaveTheSharedVarianceStyleGeneratorUnscaled() {
|
||||
assertArrayEquals(sampleUntouchedSharedVarianceStyleGenerator(), sampleSharedVarianceStyleGeneratorAfterVarianceBuilt(), 0D);
|
||||
}
|
||||
|
||||
private double[] sampleSharedVarianceStyleGeneratorAfterVarianceBuilt() {
|
||||
decorator.getVarianceGenerator(new RNG(WORLD_SEED), packData);
|
||||
return sampleSharedVarianceStyleGenerator(decorator.getVariance());
|
||||
}
|
||||
|
||||
private double[] sampleUntouchedSharedVarianceStyleGenerator() {
|
||||
return sampleSharedVarianceStyleGenerator(createZoomedSimplexStyle());
|
||||
}
|
||||
|
||||
private double[] sampleSharedVarianceStyleGenerator(IrisGeneratorStyle varianceStyle) {
|
||||
return sampleSharedGenerator(varianceStyle, decorator.deriveVarianceSeed(new RNG(WORLD_SEED), packData), packData);
|
||||
}
|
||||
|
||||
private static IrisDecorator createDecoratorWithZoomedSimplexVariance() {
|
||||
return new IrisDecorator().setPalette(new KList<IrisBlockData>()).setVariance(createZoomedSimplexStyle());
|
||||
}
|
||||
|
||||
private static IrisGeneratorStyle createZoomedSimplexStyle() {
|
||||
return new IrisGeneratorStyle(NoiseStyle.SIMPLEX).setZoom(VARIANCE_ZOOM);
|
||||
}
|
||||
}
|
||||
@@ -2,29 +2,28 @@ package art.arcane.iris.generation.terrain;
|
||||
|
||||
import art.arcane.iris.generation.noise.IrisGeneratorStyle;
|
||||
import art.arcane.iris.generation.noise.NoiseStyle;
|
||||
import art.arcane.iris.generation.runtime.Engine;
|
||||
import art.arcane.iris.pack.loading.IrisData;
|
||||
import art.arcane.iris.testsupport.RunningEnginePackData;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import art.arcane.volmlib.util.noise.CNG;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static art.arcane.iris.testsupport.GeneratorSampling.SAMPLE_BLOCKS;
|
||||
import static art.arcane.iris.testsupport.GeneratorSampling.sampleAtBlocks;
|
||||
import static art.arcane.iris.testsupport.GeneratorSampling.sampleSharedGenerator;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class ContinentGeneratorTest {
|
||||
private static final long WORLD_SEED = 7L;
|
||||
private static final double POWER_OF_TWO_CONTINENT_ZOOM = 4D;
|
||||
private static final double SQUARED_CONTINENT_ZOOM = POWER_OF_TWO_CONTINENT_ZOOM * POWER_OF_TWO_CONTINENT_ZOOM;
|
||||
private static final double OVERWORLD_STYLE_ZOOM = 6D;
|
||||
private static final double[][] SAMPLE_BLOCKS = {{1000D, -2000D}, {12.5D, -7.25D}, {-517D, 333D}};
|
||||
private static final double[][] BLOCKS_SHRUNK_BY_CONTINENT_ZOOM = shrinkByContinentZoom(SAMPLE_BLOCKS);
|
||||
private static final double[] CONTINENT_MAP_OF_EXISTING_WORLDS =
|
||||
{0.21889892332042843D, 0.6984934456740721D, 0.2708759584352327D};
|
||||
|
||||
private final IrisData packData = createPackDataThatBelongsToOneRunningEngine();
|
||||
private final IrisData packData = RunningEnginePackData.create();
|
||||
private final IrisDimension baseZoomDimension = createDimensionWithContinentZoom(POWER_OF_TWO_CONTINENT_ZOOM);
|
||||
private final IrisDimension squaredZoomDimension = createDimensionWithContinentZoom(SQUARED_CONTINENT_ZOOM);
|
||||
private final IrisDimension overworldStyleDimension = createDimensionWithOverworldContinentalStyle();
|
||||
@@ -92,21 +91,13 @@ public class ContinentGeneratorTest {
|
||||
}
|
||||
|
||||
private double[] sampleSharedStyleGenerator(IrisGeneratorStyle style) {
|
||||
return sampleAtBlocks(style.create(ContinentGenerator.deriveContinentSeed(new RNG(WORLD_SEED)), packData), SAMPLE_BLOCKS);
|
||||
return sampleSharedGenerator(style, ContinentGenerator.deriveContinentSeed(new RNG(WORLD_SEED)), packData);
|
||||
}
|
||||
|
||||
private double[] sampleContinentMap(IrisDimension dimension, double[][] blocks) {
|
||||
return sampleAtBlocks(ContinentGenerator.create(dimension, new RNG(WORLD_SEED), packData), blocks);
|
||||
}
|
||||
|
||||
private static double[] sampleAtBlocks(CNG continentGenerator, double[][] blocks) {
|
||||
return Arrays.stream(blocks).mapToDouble(block -> sampleAt(continentGenerator, block)).toArray();
|
||||
}
|
||||
|
||||
private static double sampleAt(CNG continentGenerator, double[] block) {
|
||||
return continentGenerator.noise(block[0], block[1]);
|
||||
}
|
||||
|
||||
private static double[][] shrinkByContinentZoom(double[][] blocks) {
|
||||
return Arrays.stream(blocks).map(block -> shrinkByContinentZoom(block)).toArray(double[][]::new);
|
||||
}
|
||||
@@ -134,14 +125,4 @@ public class ContinentGeneratorTest {
|
||||
private static IrisGeneratorStyle createSmokeFracture() {
|
||||
return new IrisGeneratorStyle(NoiseStyle.FRACTAL_SMOKE).setFracture(new IrisGeneratorStyle(NoiseStyle.STATIC));
|
||||
}
|
||||
|
||||
private static IrisData createPackDataThatBelongsToOneRunningEngine() {
|
||||
IrisData newPackData = mock(IrisData.class);
|
||||
bindToNewRunningEngine(newPackData);
|
||||
return newPackData;
|
||||
}
|
||||
|
||||
private static void bindToNewRunningEngine(IrisData packData) {
|
||||
when(packData.getEngine()).thenReturn(mock(Engine.class));
|
||||
}
|
||||
}
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package art.arcane.iris.generation.terrain;
|
||||
|
||||
import art.arcane.iris.generation.noise.IrisGeneratorStyle;
|
||||
import art.arcane.iris.generation.noise.NoiseStyle;
|
||||
import art.arcane.iris.pack.loading.IrisData;
|
||||
import art.arcane.iris.testsupport.RunningEnginePackData;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.junit.Test;
|
||||
|
||||
import static art.arcane.iris.testsupport.GeneratorSampling.SAMPLE_BLOCKS;
|
||||
import static art.arcane.iris.testsupport.GeneratorSampling.sampleSharedGenerator;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
public class IrisDimensionCarvingEntryTest {
|
||||
private static final long WORLD_SEED = 7L;
|
||||
private static final String CARVING_ENTRY_ID = "deep-caves";
|
||||
private static final double CHILD_SHRINK_FACTOR = 4D;
|
||||
|
||||
private final IrisData packData = RunningEnginePackData.create();
|
||||
private final IrisDimensionCarvingEntry carvingEntry = createCarvingEntry();
|
||||
|
||||
@Test
|
||||
public void doesBuildingTheChildrenGeneratorLeaveTheSharedChildStyleGeneratorUnscaled() {
|
||||
assertArrayEquals(sampleUntouchedSharedChildStyleGenerator(), sampleSharedChildStyleGeneratorAfterChildrenGeneratorBuilt(), 0D);
|
||||
}
|
||||
|
||||
private double[] sampleSharedChildStyleGeneratorAfterChildrenGeneratorBuilt() {
|
||||
carvingEntry.getChildrenGenerator(WORLD_SEED, packData);
|
||||
return sampleSharedChildStyleGenerator(carvingEntry.getChildStyle());
|
||||
}
|
||||
|
||||
private double[] sampleUntouchedSharedChildStyleGenerator() {
|
||||
return sampleSharedChildStyleGenerator(createSimplexStyle());
|
||||
}
|
||||
|
||||
private double[] sampleSharedChildStyleGenerator(IrisGeneratorStyle childStyle) {
|
||||
return sampleSharedGenerator(childStyle, deriveSharedChildStyleSeed(), packData);
|
||||
}
|
||||
|
||||
private RNG deriveSharedChildStyleSeed() {
|
||||
return IrisDimensionCarvingEntry.deriveChildStyleSeed(carvingEntry.deriveChildrenGeneratorSeed(WORLD_SEED));
|
||||
}
|
||||
|
||||
private static IrisDimensionCarvingEntry createCarvingEntry() {
|
||||
return withShrinkingSimplexChildStyle(new IrisDimensionCarvingEntry().setId(CARVING_ENTRY_ID));
|
||||
}
|
||||
|
||||
private static IrisDimensionCarvingEntry withShrinkingSimplexChildStyle(IrisDimensionCarvingEntry carvingEntry) {
|
||||
return carvingEntry.setChildStyle(createSimplexStyle()).setChildShrinkFactor(CHILD_SHRINK_FACTOR);
|
||||
}
|
||||
|
||||
private static IrisGeneratorStyle createSimplexStyle() {
|
||||
return new IrisGeneratorStyle(NoiseStyle.SIMPLEX);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package art.arcane.iris.testsupport;
|
||||
|
||||
import art.arcane.iris.generation.noise.IrisGeneratorStyle;
|
||||
import art.arcane.iris.pack.loading.IrisData;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import art.arcane.volmlib.util.noise.CNG;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class GeneratorSampling {
|
||||
public static final double[][] SAMPLE_BLOCKS = {{1000D, -2000D}, {12.5D, -7.25D}, {-517D, 333D}};
|
||||
|
||||
private GeneratorSampling() {
|
||||
}
|
||||
|
||||
public static double[] sampleSharedGenerator(IrisGeneratorStyle style, RNG seed, IrisData packData) {
|
||||
return sampleAtBlocks(style.create(seed, packData), SAMPLE_BLOCKS);
|
||||
}
|
||||
|
||||
public static double[] sampleAtBlocks(CNG generator, double[][] blocks) {
|
||||
return Arrays.stream(blocks).mapToDouble(block -> sampleAt(generator, block)).toArray();
|
||||
}
|
||||
|
||||
private static double sampleAt(CNG generator, double[] block) {
|
||||
return generator.noise(block[0], block[1]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package art.arcane.iris.testsupport;
|
||||
|
||||
import art.arcane.iris.generation.runtime.Engine;
|
||||
import art.arcane.iris.pack.loading.IrisData;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public final class RunningEnginePackData {
|
||||
private RunningEnginePackData() {
|
||||
}
|
||||
|
||||
public static IrisData create() {
|
||||
IrisData packData = mock(IrisData.class);
|
||||
when(packData.getEngine()).thenReturn(mock(Engine.class));
|
||||
return packData;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user