From deb7db9b2551e455b61ef465e11ef6066e804c74 Mon Sep 17 00:00:00 2001 From: dfsek Date: Mon, 16 Nov 2020 21:44:49 -0700 Subject: [PATCH] Per-world noise gen is no longer dumb --- build.gradle.kts | 1 + .../config/genconfig/noise/NoiseConfig.java | 3 +- .../terra/generation/ElevationEquation.java | 43 +++----- .../generation/ElevationInterpolator.java | 2 +- .../generation/UserDefinedGenerator.java | 97 +++++++------------ .../terra/generation/config/NoiseBuilder.java | 11 ++- .../generation/config/WorldGenerator.java | 65 +++++++++++++ .../com/dfsek/terra/math/BlankFunction.java | 29 ++++++ .../com/dfsek/terra/math/NoiseFunction.java | 1 - .../com/dfsek/terra/math/NoiseFunction2.java | 13 +-- .../com/dfsek/terra/math/NoiseFunction3.java | 13 +-- .../dfsek/terra/structure/spawn/AirSpawn.java | 4 +- .../terra/structure/spawn/LandSpawn.java | 4 +- .../terra/structure/spawn/OceanSpawn.java | 4 +- 14 files changed, 172 insertions(+), 118 deletions(-) create mode 100644 src/main/java/com/dfsek/terra/generation/config/WorldGenerator.java create mode 100644 src/main/java/com/dfsek/terra/math/BlankFunction.java diff --git a/build.gradle.kts b/build.gradle.kts index ac068802b..ea3cb99a1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -60,6 +60,7 @@ val mainSourceSet: SourceSet = sourceSets["main"] val tokenizeJavaSources = task(name = "tokenizeJavaSources") { from(mainSourceSet.allSource) { include("**/plugin.yml") + println("version: $versionObj") val tokens = mapOf("VERSION" to versionObj.toString()) filter(org.apache.tools.ant.filters.ReplaceTokens::class, "tokens" to tokens) diff --git a/src/main/java/com/dfsek/terra/config/genconfig/noise/NoiseConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/noise/NoiseConfig.java index 7e63ebe78..ff27b8439 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/noise/NoiseConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/noise/NoiseConfig.java @@ -14,7 +14,8 @@ public class NoiseConfig { try { builder.setType(FastNoiseLite.NoiseType.valueOf(section.getString("type", "OpenSimplex2"))) .setFrequency(section.getDouble("frequency", 0.02D)) - .setRotationType3D(FastNoiseLite.RotationType3D.valueOf(section.getString("rotation", "None"))); + .setRotationType3D(FastNoiseLite.RotationType3D.valueOf(section.getString("rotation", "None"))) + .setSeedOffset(section.getInt("offset", 0)); dimensions = section.getInt("dimensions", 3); if(dimensions != 2 && dimensions != 3) diff --git a/src/main/java/com/dfsek/terra/generation/ElevationEquation.java b/src/main/java/com/dfsek/terra/generation/ElevationEquation.java index fc18d683e..af084cc42 100644 --- a/src/main/java/com/dfsek/terra/generation/ElevationEquation.java +++ b/src/main/java/com/dfsek/terra/generation/ElevationEquation.java @@ -1,7 +1,6 @@ package com.dfsek.terra.generation; import com.dfsek.terra.config.genconfig.noise.NoiseConfig; -import com.dfsek.terra.math.NoiseFunction; import com.dfsek.terra.math.NoiseFunction2; import com.dfsek.terra.math.NoiseFunction3; import org.bukkit.World; @@ -11,55 +10,43 @@ import parsii.eval.Scope; import parsii.eval.Variable; import parsii.tokenizer.ParseException; -import java.util.ArrayList; -import java.util.List; import java.util.Map; public class ElevationEquation { - private static final Object noiseLock = new Object(); private final Expression delegate; private final Scope s = new Scope(); private final Variable xVar = s.getVariable("x"); private final Variable zVar = s.getVariable("z"); - private final List noiseFunctions = new ArrayList<>(); - private boolean set = true; - - public ElevationEquation(String equation, Map noiseBuilders) throws ParseException { + public ElevationEquation(World w, String elevateEquation, Map userVariables, Map noiseBuilders) { + for(Map.Entry entry : userVariables.entrySet()) { + s.getVariable(entry.getKey()).setValue(entry.getValue()); // Define all user variables. + } Parser p = new Parser(); + for(Map.Entry e : noiseBuilders.entrySet()) { switch(e.getValue().getDimensions()) { case 2: - NoiseFunction2 function2 = new NoiseFunction2(e.getValue().getBuilder()); - noiseFunctions.add(function2); + NoiseFunction2 function2 = new NoiseFunction2(w, e.getValue().getBuilder()); p.registerFunction(e.getKey(), function2); break; case 3: - NoiseFunction3 function3 = new NoiseFunction3(e.getValue().getBuilder()); - noiseFunctions.add(function3); + NoiseFunction3 function3 = new NoiseFunction3(w, e.getValue().getBuilder()); p.registerFunction(e.getKey(), function3); break; } } - delegate = p.parse(equation, s); - } - - public double getNoise(double x, double z, World w) { - synchronized(noiseLock) { - xVar.setValue(x); - zVar.setValue(z); - setNoise(w.getSeed()); - return delegate.evaluate(); + try { + this.delegate = p.parse(elevateEquation, s); + } catch(ParseException e) { + throw new IllegalArgumentException(); } } - private void setNoise(long seed) { - if(set) { - set = false; - for(NoiseFunction n : noiseFunctions) { - n.setNoise(seed); - } - } + public double getNoise(double x, double z) { + xVar.setValue(x); + zVar.setValue(z); + 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 index eab81feb1..428ade7fc 100644 --- a/src/main/java/com/dfsek/terra/generation/ElevationInterpolator.java +++ b/src/main/java/com/dfsek/terra/generation/ElevationInterpolator.java @@ -70,7 +70,7 @@ public class ElevationInterpolator { } private double elevate(UserDefinedGenerator g, int x, int z, World w) { - if(g.getElevationEquation() != null) return g.getElevationEquation().getNoise(x, z, w); + if(g.getElevationEquation(w) != null) return g.getElevationEquation(w).getNoise(x, z); return 0; } diff --git a/src/main/java/com/dfsek/terra/generation/UserDefinedGenerator.java b/src/main/java/com/dfsek/terra/generation/UserDefinedGenerator.java index 9e72e1638..b067a149b 100644 --- a/src/main/java/com/dfsek/terra/generation/UserDefinedGenerator.java +++ b/src/main/java/com/dfsek/terra/generation/UserDefinedGenerator.java @@ -1,10 +1,8 @@ package com.dfsek.terra.generation; -import com.dfsek.terra.Debug; import com.dfsek.terra.config.genconfig.noise.NoiseConfig; -import com.dfsek.terra.math.NoiseFunction; -import com.dfsek.terra.math.NoiseFunction2; -import com.dfsek.terra.math.NoiseFunction3; +import com.dfsek.terra.generation.config.WorldGenerator; +import com.dfsek.terra.math.BlankFunction; import com.dfsek.terra.util.DataUtil; import org.bukkit.World; import org.bukkit.block.data.BlockData; @@ -13,56 +11,53 @@ import org.polydev.gaea.biome.Generator; import org.polydev.gaea.math.FastNoiseLite; import org.polydev.gaea.math.Interpolator; import org.polydev.gaea.world.palette.Palette; -import parsii.eval.Expression; import parsii.eval.Parser; import parsii.eval.Scope; -import parsii.eval.Variable; import parsii.tokenizer.ParseException; -import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class UserDefinedGenerator extends Generator { - private static final Object noiseLock = new Object(); - private final Expression noiseExp; - private final Scope s = new Scope(); - private final Variable xVar = s.getVariable("x"); - private final Variable yVar = s.getVariable("y"); - private final Variable zVar = s.getVariable("z"); + + private final boolean preventSmooth; @SuppressWarnings({"unchecked", "rawtypes", "RedundantSuppression"}) private final Palette[] palettes = new Palette[256]; @SuppressWarnings({"unchecked", "rawtypes", "RedundantSuppression"}) private final Palette[] slantPalettes = new Palette[256]; - private final ElevationEquation elevationEquation; - private final boolean preventSmooth; + + + private final String equation; + private final String elevationEquation; + private final Map userVariables; + private final Map noiseBuilders; + + private final Map gens = new HashMap<>(); private boolean elevationInterpolation; - private final List noiseFunctions = new ArrayList<>(); - private boolean set = true; public UserDefinedGenerator(String equation, @Nullable String elevateEquation, Map userVariables, Map> paletteMap, Map> slantPaletteMap, Map noiseBuilders, boolean preventSmooth) throws ParseException { + this.equation = equation; + this.elevationEquation = elevateEquation; + this.userVariables = userVariables; + this.noiseBuilders = noiseBuilders; + this.preventSmooth = preventSmooth; + + Scope s = new Scope(); + Parser p = new Parser(); for(Map.Entry entry : userVariables.entrySet()) { s.getVariable(entry.getKey()).setValue(entry.getValue()); // Define all user variables. } - Parser p = new Parser(); - for(Map.Entry e : noiseBuilders.entrySet()) { - switch(e.getValue().getDimensions()) { - case 2: - NoiseFunction2 function2 = new NoiseFunction2(e.getValue().getBuilder()); - noiseFunctions.add(function2); - p.registerFunction(e.getKey(), function2); - break; - case 3: - NoiseFunction3 function3 = new NoiseFunction3(e.getValue().getBuilder()); - noiseFunctions.add(function3); - p.registerFunction(e.getKey(), function3); - break; - } + int dimensions = e.getValue().getDimensions(); + if(dimensions == 2 || dimensions == 3) p.registerFunction(e.getKey(), new BlankFunction(dimensions)); } + p.parse(equation, s); // Validate equation at config load time to prevent error during world load. + if(elevateEquation != null) p.parse(elevateEquation, s); + for(int y = 0; y < 256; y++) { Palette d = DataUtil.BLANK_PALETTE; @@ -82,21 +77,7 @@ public class UserDefinedGenerator extends Generator { } slantPalettes[y] = slantPalette; } - if(elevateEquation != null) { - Debug.info("Using elevation equation"); - this.elevationEquation = new ElevationEquation(elevateEquation, noiseBuilders); - } else this.elevationEquation = null; - this.noiseExp = p.parse(equation, s); - this.preventSmooth = preventSmooth; - } - private void setNoise(long seed) { - if(set) { - set = false; - for(NoiseFunction n : noiseFunctions) { - n.setNoise(seed); - } - } } /** @@ -109,13 +90,7 @@ public class UserDefinedGenerator extends Generator { */ @Override public double getNoise(FastNoiseLite gen, World w, int x, int z) { - synchronized(noiseLock) { - xVar.setValue(x); - yVar.setValue(0); - zVar.setValue(z); - setNoise(w.getSeed()); - return noiseExp.evaluate(); - } + return compute(w).getNoise(x, 0, z); } /** @@ -129,13 +104,11 @@ public class UserDefinedGenerator extends Generator { */ @Override public double getNoise(FastNoiseLite gen, World w, int x, int y, int z) { - synchronized(noiseLock) { - xVar.setValue(x); - yVar.setValue(y); - zVar.setValue(z); - setNoise(w.getSeed()); - return noiseExp.evaluate(); - } + return compute(w).getNoise(x, y, z); + } + + private WorldGenerator compute(World world) { + return gens.computeIfAbsent(world.getUID(), w -> new WorldGenerator(world, equation, elevationEquation, userVariables, noiseBuilders)); } /** @@ -163,8 +136,8 @@ public class UserDefinedGenerator extends Generator { return Interpolator.Type.LINEAR; } - public ElevationEquation getElevationEquation() { - return elevationEquation; + public ElevationEquation getElevationEquation(World w) { + return compute(w).getElevationEquation(); } public boolean interpolateElevation() { diff --git a/src/main/java/com/dfsek/terra/generation/config/NoiseBuilder.java b/src/main/java/com/dfsek/terra/generation/config/NoiseBuilder.java index 31413e844..927162922 100644 --- a/src/main/java/com/dfsek/terra/generation/config/NoiseBuilder.java +++ b/src/main/java/com/dfsek/terra/generation/config/NoiseBuilder.java @@ -11,6 +11,7 @@ public class NoiseBuilder { private double fractalLacunarity = 2.0D; private double pingPong = 2.0D; private double weightedStrength = 0.0D; + private int seedOffset = 0; private FastNoiseLite.CellularDistanceFunction cellularDistanceFunction = FastNoiseLite.CellularDistanceFunction.EuclideanSq; private FastNoiseLite.CellularReturnType cellularReturnType = FastNoiseLite.CellularReturnType.Distance; @@ -22,7 +23,7 @@ public class NoiseBuilder { private FastNoiseLite.RotationType3D rotationType3D = FastNoiseLite.RotationType3D.None; public FastNoiseLite build(int seed) { - FastNoiseLite noise = new FastNoiseLite(seed); + FastNoiseLite noise = new FastNoiseLite(seed + seedOffset); if(!fractalType.equals(FastNoiseLite.FractalType.None)) { noise.setFractalType(fractalType); noise.setFractalOctaves(octaves); @@ -107,6 +108,14 @@ public class NoiseBuilder { return this; } + public int getSeedOffset() { + return seedOffset; + } + + public void setSeedOffset(int seedOffset) { + this.seedOffset = seedOffset; + } + public NoiseBuilder setDomainWarpAmp(double domainWarpAmp) { this.domainWarpAmp = domainWarpAmp; return this; diff --git a/src/main/java/com/dfsek/terra/generation/config/WorldGenerator.java b/src/main/java/com/dfsek/terra/generation/config/WorldGenerator.java new file mode 100644 index 000000000..51a72a5ea --- /dev/null +++ b/src/main/java/com/dfsek/terra/generation/config/WorldGenerator.java @@ -0,0 +1,65 @@ +package com.dfsek.terra.generation.config; + +import com.dfsek.terra.Debug; +import com.dfsek.terra.config.genconfig.noise.NoiseConfig; +import com.dfsek.terra.generation.ElevationEquation; +import com.dfsek.terra.math.NoiseFunction2; +import com.dfsek.terra.math.NoiseFunction3; +import org.bukkit.World; +import parsii.eval.Expression; +import parsii.eval.Parser; +import parsii.eval.Scope; +import parsii.eval.Variable; +import parsii.tokenizer.ParseException; + +import java.util.Map; + +public class WorldGenerator { + private final ElevationEquation elevationEquation; + + private final Expression noiseExp; + private final Scope s = new Scope(); + private final Variable xVar = s.getVariable("x"); + private final Variable yVar = s.getVariable("y"); + private final Variable zVar = s.getVariable("z"); + + public WorldGenerator(World w, String equation, String elevateEquation, Map userVariables, Map noiseBuilders) { + for(Map.Entry entry : userVariables.entrySet()) { + s.getVariable(entry.getKey()).setValue(entry.getValue()); // Define all user variables. + } + Parser p = new Parser(); + + for(Map.Entry e : noiseBuilders.entrySet()) { + switch(e.getValue().getDimensions()) { + case 2: + NoiseFunction2 function2 = new NoiseFunction2(w, e.getValue().getBuilder()); + p.registerFunction(e.getKey(), function2); + break; + case 3: + NoiseFunction3 function3 = new NoiseFunction3(w, e.getValue().getBuilder()); + p.registerFunction(e.getKey(), function3); + break; + } + } + try { + this.noiseExp = p.parse(equation, s); + if(elevateEquation != null) { + Debug.info("Using elevation equation"); + this.elevationEquation = new ElevationEquation(w, elevateEquation, userVariables, noiseBuilders); + } else this.elevationEquation = null; + } catch(ParseException e) { + throw new IllegalArgumentException(); + } + } + + public double getNoise(int x, int y, int z) { + xVar.setValue(x); + yVar.setValue(y); + zVar.setValue(z); + return noiseExp.evaluate(); + } + + public ElevationEquation getElevationEquation() { + return elevationEquation; + } +} diff --git a/src/main/java/com/dfsek/terra/math/BlankFunction.java b/src/main/java/com/dfsek/terra/math/BlankFunction.java new file mode 100644 index 000000000..def469f97 --- /dev/null +++ b/src/main/java/com/dfsek/terra/math/BlankFunction.java @@ -0,0 +1,29 @@ +package com.dfsek.terra.math; + +import parsii.eval.Expression; +import parsii.eval.Function; + +import java.util.List; + +public class BlankFunction implements Function { + private final int args; + + public BlankFunction(int args) { + this.args = args; + } + + @Override + public int getNumberOfArguments() { + return args; + } + + @Override + public double eval(List list) { + return 0; + } + + @Override + public boolean isNaturalFunction() { + return true; + } +} diff --git a/src/main/java/com/dfsek/terra/math/NoiseFunction.java b/src/main/java/com/dfsek/terra/math/NoiseFunction.java index c6685666a..ccc604c9e 100644 --- a/src/main/java/com/dfsek/terra/math/NoiseFunction.java +++ b/src/main/java/com/dfsek/terra/math/NoiseFunction.java @@ -3,5 +3,4 @@ package com.dfsek.terra.math; import parsii.eval.Function; public interface NoiseFunction extends Function { - void setNoise(long seed); } diff --git a/src/main/java/com/dfsek/terra/math/NoiseFunction2.java b/src/main/java/com/dfsek/terra/math/NoiseFunction2.java index 3c15a18b4..08dc65c7c 100644 --- a/src/main/java/com/dfsek/terra/math/NoiseFunction2.java +++ b/src/main/java/com/dfsek/terra/math/NoiseFunction2.java @@ -2,6 +2,7 @@ package com.dfsek.terra.math; import com.dfsek.terra.config.base.ConfigUtil; import com.dfsek.terra.generation.config.NoiseBuilder; +import org.bukkit.World; import org.polydev.gaea.math.FastNoiseLite; import parsii.eval.Expression; @@ -9,11 +10,10 @@ import java.util.List; public class NoiseFunction2 implements NoiseFunction { private final Cache cache = new Cache(); - private FastNoiseLite gen; - private final NoiseBuilder builder; + private final FastNoiseLite gen; - public NoiseFunction2(NoiseBuilder builder) { - this.builder = builder; + public NoiseFunction2(World world, NoiseBuilder builder) { + this.gen = builder.build((int) world.getSeed()); } @Override @@ -31,11 +31,6 @@ public class NoiseFunction2 implements NoiseFunction { return true; } - @Override - public void setNoise(long seed) { - this.gen = builder.build((int) seed); - } - private final class Cache { private final double[] cacheX = new double[ConfigUtil.cacheSize]; private final double[] cacheZ = new double[ConfigUtil.cacheSize]; diff --git a/src/main/java/com/dfsek/terra/math/NoiseFunction3.java b/src/main/java/com/dfsek/terra/math/NoiseFunction3.java index 55840d60e..e9294c79d 100644 --- a/src/main/java/com/dfsek/terra/math/NoiseFunction3.java +++ b/src/main/java/com/dfsek/terra/math/NoiseFunction3.java @@ -1,17 +1,17 @@ package com.dfsek.terra.math; import com.dfsek.terra.generation.config.NoiseBuilder; +import org.bukkit.World; import org.polydev.gaea.math.FastNoiseLite; import parsii.eval.Expression; import java.util.List; public class NoiseFunction3 implements NoiseFunction { - private FastNoiseLite gen; - private final NoiseBuilder builder; + private final FastNoiseLite gen; - public NoiseFunction3(NoiseBuilder builder) { - this.builder = builder; + public NoiseFunction3(World world, NoiseBuilder builder) { + this.gen = builder.build((int) world.getSeed()); } @Override @@ -28,9 +28,4 @@ public class NoiseFunction3 implements NoiseFunction { public boolean isNaturalFunction() { return true; } - - @Override - public void setNoise(long seed) { - this.gen = builder.build((int) seed); - } } diff --git a/src/main/java/com/dfsek/terra/structure/spawn/AirSpawn.java b/src/main/java/com/dfsek/terra/structure/spawn/AirSpawn.java index 2df85238a..a2fc71115 100644 --- a/src/main/java/com/dfsek/terra/structure/spawn/AirSpawn.java +++ b/src/main/java/com/dfsek/terra/structure/spawn/AirSpawn.java @@ -21,8 +21,8 @@ public class AirSpawn extends Requirement { UserDefinedBiome b = (UserDefinedBiome) tw.getGrid().getBiome(x, z, GenerationPhase.POPULATE); BiomeConfig c = wc.getBiome(b); if(y <= c.getOcean().getSeaLevel()) return false; - ElevationEquation elevationEquation = ((UserDefinedGenerator) b.getGenerator()).getElevationEquation(); - int yf = y - ((elevationEquation == null) ? 0 : (int) elevationEquation.getNoise(x, z, getWorld())); + ElevationEquation elevationEquation = ((UserDefinedGenerator) b.getGenerator()).getElevationEquation(getWorld()); + int yf = y - ((elevationEquation == null) ? 0 : (int) elevationEquation.getNoise(x, z)); return b.getGenerator().getNoise(getNoise(), getWorld(), x, yf, z) <= 0; } } diff --git a/src/main/java/com/dfsek/terra/structure/spawn/LandSpawn.java b/src/main/java/com/dfsek/terra/structure/spawn/LandSpawn.java index 6c6da9162..1158c3f65 100644 --- a/src/main/java/com/dfsek/terra/structure/spawn/LandSpawn.java +++ b/src/main/java/com/dfsek/terra/structure/spawn/LandSpawn.java @@ -16,8 +16,8 @@ public class LandSpawn extends Requirement { public boolean matches(int x, int y, int z) { TerraWorld tw = TerraWorld.getWorld(getWorld()); UserDefinedBiome b = (UserDefinedBiome) tw.getGrid().getBiome(x, z, GenerationPhase.POPULATE); - ElevationEquation elevationEquation = ((UserDefinedGenerator) b.getGenerator()).getElevationEquation(); - int yf = y - ((elevationEquation == null) ? 0 : (int) elevationEquation.getNoise(x, z, getWorld())); + ElevationEquation elevationEquation = ((UserDefinedGenerator) b.getGenerator()).getElevationEquation(getWorld()); + int yf = y - ((elevationEquation == null) ? 0 : (int) elevationEquation.getNoise(x, z)); return b.getGenerator().getNoise(getNoise(), getWorld(), x, yf, z) > 0; } } diff --git a/src/main/java/com/dfsek/terra/structure/spawn/OceanSpawn.java b/src/main/java/com/dfsek/terra/structure/spawn/OceanSpawn.java index 85249dbef..25d247cc5 100644 --- a/src/main/java/com/dfsek/terra/structure/spawn/OceanSpawn.java +++ b/src/main/java/com/dfsek/terra/structure/spawn/OceanSpawn.java @@ -19,8 +19,8 @@ public class OceanSpawn extends Requirement { UserDefinedBiome b = (UserDefinedBiome) tw.getGrid().getBiome(x, z, GenerationPhase.POPULATE); BiomeConfig c = tw.getConfig().getBiome(b); if(y > c.getOcean().getSeaLevel()) return false; - ElevationEquation elevationEquation = ((UserDefinedGenerator) b.getGenerator()).getElevationEquation(); - int yf = y - ((elevationEquation == null) ? 0 : (int) elevationEquation.getNoise(x, z, getWorld())); + ElevationEquation elevationEquation = ((UserDefinedGenerator) b.getGenerator()).getElevationEquation(getWorld()); + int yf = y - ((elevationEquation == null) ? 0 : (int) elevationEquation.getNoise(x, z)); return b.getGenerator().getNoise(getNoise(), getWorld(), x, yf, z) <= 0; } }