From 922f971c8ecb077b94d632bdab67505b47a20a38 Mon Sep 17 00:00:00 2001 From: dfsek Date: Wed, 11 Nov 2020 02:28:44 -0700 Subject: [PATCH] Implement Elevation --- .../biome/failsafe/FailoverGenerator.java | 2 +- .../config/genconfig/biome/BiomeConfig.java | 5 +- .../terra/generation/ElevationEquation.java | 35 +++++++++ .../generation/ElevationInterpolator.java | 77 +++++++++++++++++++ .../terra/generation/TerraChunkGenerator.java | 26 ++++--- .../generation/UserDefinedGenerator.java | 13 +++- .../biomes/mountain/arid_mountains.yml | 3 +- .../java/NoiseInstancePerformanceTest.java | 17 ++++ 8 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/dfsek/terra/generation/ElevationEquation.java create mode 100644 src/main/java/com/dfsek/terra/generation/ElevationInterpolator.java create mode 100644 src/test/java/NoiseInstancePerformanceTest.java diff --git a/src/main/java/com/dfsek/terra/biome/failsafe/FailoverGenerator.java b/src/main/java/com/dfsek/terra/biome/failsafe/FailoverGenerator.java index d4b35d4db..37f3d7585 100644 --- a/src/main/java/com/dfsek/terra/biome/failsafe/FailoverGenerator.java +++ b/src/main/java/com/dfsek/terra/biome/failsafe/FailoverGenerator.java @@ -19,6 +19,6 @@ public final class FailoverGenerator extends UserDefinedGenerator { } public FailoverGenerator() throws ParseException { - super("0", Collections.emptyList(), palette, false); + super("0", null, Collections.emptyList(), palette, false); } } diff --git a/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeConfig.java index 3f86bf3cc..5ea218007 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeConfig.java @@ -168,9 +168,12 @@ public class BiomeConfig extends TerraConfig { } } + String elevation = getString("elevation-equation", null); + try { // Get UserDefinedBiome instance representing this config. - this.biome = new UserDefinedBiome(vanillaBiome, dec, new UserDefinedGenerator(eq, Collections.emptyList(), palette.getPaletteMap(), getBoolean("prevent-smooth", false)), getBoolean("erodible", false), biomeID); + UserDefinedGenerator gen = new UserDefinedGenerator(eq, elevation, Collections.emptyList(), palette.getPaletteMap(), getBoolean("prevent-smooth", false)); + this.biome = new UserDefinedBiome(vanillaBiome, dec, gen, getBoolean("erodible", false), biomeID); } catch(ParseException e) { e.printStackTrace(); throw new ConfigException("Unable to parse noise equation!", getID()); diff --git a/src/main/java/com/dfsek/terra/generation/ElevationEquation.java b/src/main/java/com/dfsek/terra/generation/ElevationEquation.java new file mode 100644 index 000000000..8cff5e1fb --- /dev/null +++ b/src/main/java/com/dfsek/terra/generation/ElevationEquation.java @@ -0,0 +1,35 @@ +package com.dfsek.terra.generation; + +import com.dfsek.terra.math.NoiseFunction2; +import org.polydev.gaea.math.FastNoiseLite; +import parsii.eval.Expression; +import parsii.eval.Parser; +import parsii.eval.Scope; +import parsii.eval.Variable; +import parsii.tokenizer.ParseException; + +public class ElevationEquation { + private static final Object noiseLock = new Object(); + private final Expression delegate; + private final Scope s = new Scope(); + private final NoiseFunction2 n2 = new NoiseFunction2(); + + private final Variable xVar = s.getVariable("x"); + private final Variable zVar = s.getVariable("z"); + + public ElevationEquation(String equation) throws ParseException { + Parser p = new Parser(); + p.registerFunction("noise2", n2); + delegate = p.parse(equation, s); + } + + public double getNoise(double x, double z, FastNoiseLite noiseLite) { + synchronized(noiseLock) { + xVar.setValue(x); + zVar.setValue(z); + + n2.setNoise(noiseLite); + return delegate.evaluate(); + } + } +} diff --git a/src/main/java/com/dfsek/terra/generation/ElevationInterpolator.java b/src/main/java/com/dfsek/terra/generation/ElevationInterpolator.java new file mode 100644 index 000000000..5cdaf60ee --- /dev/null +++ b/src/main/java/com/dfsek/terra/generation/ElevationInterpolator.java @@ -0,0 +1,77 @@ +package com.dfsek.terra.generation; + +import com.dfsek.terra.biome.TerraBiomeGrid; +import org.bukkit.World; +import org.polydev.gaea.biome.Generator; +import org.polydev.gaea.generation.GenerationPhase; +import org.polydev.gaea.math.FastNoiseLite; +import org.polydev.gaea.math.Interpolator; + +public class ElevationInterpolator { + private final UserDefinedGenerator[][] gens = new UserDefinedGenerator[7][7]; + private final double[][] values = new double[16][16]; + private final FastNoiseLite noise; + private final int xOrigin; + private final int zOrigin; + + public ElevationInterpolator(World w, int chunkX, int chunkZ, TerraBiomeGrid grid, FastNoiseLite noise) { + this.xOrigin = chunkX << 4; + this.zOrigin = chunkZ << 4; + this.noise = noise; + + for(int x = -1; x < 6; x++) { + for(int z = -1; z < 6; z++) { + gens[x + 1][z + 1] = (UserDefinedGenerator) grid.getBiome(xOrigin + x * 4, zOrigin + z * 4, GenerationPhase.BASE).getGenerator(); + } + } + + for(byte x = 0; x < 16; x++) { + for(byte z = 0; z < 16; z++) { + if(compareGens((x / 4) + 1, (z / 4) + 1)) { + Interpolator interpolator = new Interpolator(biomeAvg(x / 4, z / 4), + biomeAvg((x / 4) + 1, z / 4), + biomeAvg(x / 4, (z / 4) + 1), + biomeAvg((x / 4) + 1, (z / 4) + 1), + Interpolator.Type.LINEAR); + values[x][z] = interpolator.bilerp((double) (x % 4) / 4, (double) (z % 4) / 4); + } else values[x][z] = elevate(gens[x / 4][z / 4], xOrigin + x, zOrigin + z); + } + } + } + + private boolean compareGens(int x, int z) { + Generator comp = gens[x][z]; + if(!comp.equals(gens[x + 1][z])) return true; + + if(!comp.equals(gens[x][z + 1])) return true; + + if(!comp.equals(gens[x - 1][z])) return true; + + if(!comp.equals(gens[x][z - 1])) return true; + + if(!comp.equals(gens[x + 1][z + 1])) return true; + + if(!comp.equals(gens[x - 1][z - 1])) return true; + + if(!comp.equals(gens[x + 1][z - 1])) return true; + + return !comp.equals(gens[x - 1][z + 1]); + } + + private double biomeAvg(int x, int z) { + return (elevate(gens[x + 2][z + 1], x * 4 + 4 + xOrigin, z * 4 + zOrigin) + + elevate(gens[x][z + 1], x * 4 - 4 + xOrigin, z * 4 + zOrigin) + + elevate(gens[x + 1][z + 2], x * 4 + xOrigin, z * 4 + 4 + zOrigin) + + elevate(gens[x + 1][z], x * 4 + xOrigin, z * 4 - 4 + zOrigin) + + elevate(gens[x + 1][z + 1], x * 4 + xOrigin, z * 4 + zOrigin)) / 5D; + } + + private double elevate(UserDefinedGenerator g, int x, int z) { + if(g.getElevationEquation() != null) return g.getElevationEquation().getNoise(x, z, noise); + return 0; + } + + public double getElevation(int x, int z) { + return values[x][z]; + } +} diff --git a/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java b/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java index c230e6e94..62aa6e9bb 100644 --- a/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java +++ b/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java @@ -80,19 +80,19 @@ public class TerraChunkGenerator extends GaeaChunkGenerator { popMan.attachProfiler(p); } - private static Palette getPalette(int x, int y, int z, BiomeConfig c, ChunkInterpolator interpolator) { + private static Palette getPalette(int x, int y, int z, BiomeConfig c, ChunkInterpolator interpolator, int elevate) { Palette slant = c.getSlant(); if(slant != null) { double xzOffset = c.getXZSlantOffset(); - boolean north = interpolator.getNoise(x, y, z + xzOffset) > 0; - boolean south = interpolator.getNoise(x, y, z - xzOffset) > 0; - boolean east = interpolator.getNoise(x + xzOffset, y, z) > 0; - boolean west = interpolator.getNoise(x - xzOffset, y, z) > 0; + boolean north = interpolator.getNoise(x, y + elevate, z + xzOffset) > 0; + boolean south = interpolator.getNoise(x, y + elevate, z - xzOffset) > 0; + boolean east = interpolator.getNoise(x + xzOffset, y + elevate, z) > 0; + boolean west = interpolator.getNoise(x - xzOffset, y + elevate, z) > 0; double ySlantOffsetTop = c.getYSlantOffsetTop(); double ySlantOffsetBottom = c.getYSlantOffsetBottom(); - boolean top = interpolator.getNoise(x, y + ySlantOffsetTop, z) > 0; - boolean bottom = interpolator.getNoise(x, y - ySlantOffsetBottom, z) > 0; + boolean top = interpolator.getNoise(x, y + ySlantOffsetTop + elevate, z) > 0; + boolean bottom = interpolator.getNoise(x, y - ySlantOffsetBottom + elevate, z) > 0; if((top && bottom) && (north || south || east || west) && (!(north && south && east && west))) return slant; } @@ -141,19 +141,27 @@ public class TerraChunkGenerator extends GaeaChunkGenerator { int xOrig = (chunkX << 4); int zOrig = (chunkZ << 4); org.polydev.gaea.biome.BiomeGrid grid = getBiomeGrid(world); + + ElevationInterpolator elevationInterpolator = new ElevationInterpolator(world, chunkX, chunkZ, tw.getGrid(), getNoiseGenerator()); + for(byte x = 0; x < 16; x++) { for(byte z = 0; z < 16; z++) { int paletteLevel = 0; + int cx = xOrig + x; int cz = zOrig + z; + Biome b = grid.getBiome(xOrig + x, zOrig + z, GenerationPhase.PALETTE_APPLY); BiomeConfig c = config.getBiome((UserDefinedBiome) b); + + int elevate = (int) elevationInterpolator.getElevation(x, z); + BiomeSlabConfig slab = c.getSlabs(); int sea = c.getOcean().getSeaLevel(); Palette seaPalette = c.getOcean().getOcean(); for(int y = world.getMaxHeight() - 1; y >= 0; y--) { - if(interpolator.getNoise(x, y, z) > 0) { - BlockData data = getPalette(x, y, z, c, interpolator).get(paletteLevel, cx, cz); + if(interpolator.getNoise(x, y - elevate, z) > 0) { + BlockData data = getPalette(x, y, z, c, interpolator, elevate).get(paletteLevel, cx, cz); chunk.setBlock(x, y, z, data); if(paletteLevel == 0 && slab != null && y < 255) { prepareBlockPart(data, chunk.getBlockData(x, y + 1, z), chunk, new Vector(x, y + 1, z), slab.getSlabs(), diff --git a/src/main/java/com/dfsek/terra/generation/UserDefinedGenerator.java b/src/main/java/com/dfsek/terra/generation/UserDefinedGenerator.java index f3fcd13de..3bef3664f 100644 --- a/src/main/java/com/dfsek/terra/generation/UserDefinedGenerator.java +++ b/src/main/java/com/dfsek/terra/generation/UserDefinedGenerator.java @@ -1,10 +1,12 @@ package com.dfsek.terra.generation; +import com.dfsek.terra.Debug; import com.dfsek.terra.math.NoiseFunction2; import com.dfsek.terra.math.NoiseFunction3; import com.dfsek.terra.util.DataUtil; import org.bukkit.World; import org.bukkit.block.data.BlockData; +import org.jetbrains.annotations.Nullable; import org.polydev.gaea.biome.Generator; import org.polydev.gaea.math.FastNoiseLite; import org.polydev.gaea.math.Interpolator; @@ -30,10 +32,11 @@ public class UserDefinedGenerator extends Generator { private final Palette[] palettes = new Palette[256]; private final NoiseFunction2 n2 = new NoiseFunction2(); private final NoiseFunction3 n3 = new NoiseFunction3(); + private final ElevationEquation elevationEquation; private final boolean preventSmooth; - public UserDefinedGenerator(String equation, List userVariables, Map> paletteMap, boolean preventSmooth) + public UserDefinedGenerator(String equation, @Nullable String elevateEquation, List userVariables, Map> paletteMap, boolean preventSmooth) throws ParseException { Parser p = new Parser(); p.registerFunction("noise2", n2); @@ -48,6 +51,10 @@ public class UserDefinedGenerator extends Generator { } palettes[y] = d; } + if(elevateEquation != null) { + Debug.info("Using elevation equation"); + this.elevationEquation = new ElevationEquation(elevateEquation); + } else this.elevationEquation = null; this.noiseExp = p.parse(equation, s); this.preventSmooth = preventSmooth; } @@ -112,4 +119,8 @@ public class UserDefinedGenerator extends Generator { public Interpolator.Type getInterpolationType() { return Interpolator.Type.LINEAR; } + + public ElevationEquation getElevationEquation() { + return elevationEquation; + } } diff --git a/src/main/resources/default-config/biomes/mountain/arid_mountains.yml b/src/main/resources/default-config/biomes/mountain/arid_mountains.yml index 8f9a3b0dc..d06a42d0d 100644 --- a/src/main/resources/default-config/biomes/mountain/arid_mountains.yml +++ b/src/main/resources/default-config/biomes/mountain/arid_mountains.yml @@ -1,4 +1,5 @@ -noise-equation: "((-((y / 64)^2)) + 1) + min(floor(((|noise2(x/2.5, z/2.5)|) + 0.1)*4), 3)/2.5 + |noise2(x/2.5, z/2.5)|" +noise-equation: "((-((y / 64)^2)) + 1) + |noise2(x/2.5, z/2.5)|" +elevation-equation: "min(floor(((|noise2(x/2.5, z/2.5)|) + 0.1)*4)*5, 15)" id: "ARID_MOUNTAINS" extends: "BASIC_ORES" diff --git a/src/test/java/NoiseInstancePerformanceTest.java b/src/test/java/NoiseInstancePerformanceTest.java new file mode 100644 index 000000000..ac3ecc72c --- /dev/null +++ b/src/test/java/NoiseInstancePerformanceTest.java @@ -0,0 +1,17 @@ +import org.junit.jupiter.api.Test; +import org.polydev.gaea.math.FastNoiseLite; + +public class NoiseInstancePerformanceTest { + @Test + public void performance() { + FastNoiseLite noiseLite = new FastNoiseLite(2403); + long l = System.nanoTime(); + for(int i = 0; i < 10000000; i++) noiseLite.getNoise(i, i); + System.out.println("No instantiation: " + (System.nanoTime() - l) / 1000000 + "ms"); + for(int i = 0; i < 10000000; i++) { + FastNoiseLite noiseLite1 = new FastNoiseLite(2403); + noiseLite1.getNoise(i, i); + } + System.out.println("Instantiation: " + (System.nanoTime() - l) / 1000000 + "ms"); + } +}