refactor, cleanup, and perf improvements

This commit is contained in:
dfsek
2021-01-05 19:21:42 -07:00
parent 4ad2db3ca8
commit 47cad8a30b
98 changed files with 178 additions and 305 deletions
@@ -1,6 +1,7 @@
package com.dfsek.terra.api;
import com.dfsek.tectonic.loading.TypeRegistry;
import com.dfsek.terra.api.math.GridSpawn;
import com.dfsek.terra.api.math.ProbabilityCollection;
import com.dfsek.terra.api.math.Range;
import com.dfsek.terra.api.platform.TerraPlugin;
@@ -22,14 +23,13 @@ import com.dfsek.terra.config.loaders.palette.CarverPaletteLoader;
import com.dfsek.terra.config.loaders.palette.PaletteHolderLoader;
import com.dfsek.terra.config.loaders.palette.PaletteLayerLoader;
import com.dfsek.terra.generation.config.NoiseBuilder;
import com.dfsek.terra.generation.items.flora.FloraLayer;
import com.dfsek.terra.generation.items.flora.TerraFlora;
import com.dfsek.terra.generation.items.ores.Ore;
import com.dfsek.terra.generation.items.ores.OreConfig;
import com.dfsek.terra.generation.items.ores.OreHolder;
import com.dfsek.terra.generation.items.tree.TreeLayer;
import com.dfsek.terra.image.ImageLoader;
import com.dfsek.terra.procgen.GridSpawn;
import com.dfsek.terra.population.items.flora.FloraLayer;
import com.dfsek.terra.population.items.flora.TerraFlora;
import com.dfsek.terra.population.items.ores.Ore;
import com.dfsek.terra.population.items.ores.OreConfig;
import com.dfsek.terra.population.items.ores.OreHolder;
import com.dfsek.terra.population.items.tree.TreeLayer;
import com.dfsek.terra.util.MaterialSet;
public class GenericLoaders implements LoaderRegistrar {
@@ -51,7 +51,7 @@ public class GenericLoaders implements LoaderRegistrar {
.registerLoader(Ore.Type.class, (t, o, l) -> Ore.Type.valueOf((String) o))
.registerLoader(OreConfig.class, new OreConfigLoader())
.registerLoader(NoiseBuilder.class, new NoiseBuilderLoader())
.registerLoader(TreeLayer.class, new TreeLayerLoader(main))
.registerLoader(TreeLayer.class, new TreeLayerLoader())
.registerLoader(MaterialSet.class, new MaterialSetLoader())
.registerLoader(OreHolder.class, new OreHolderLoader())
.registerLoader(ImageLoader.class, new ImageLoaderLoader())
@@ -1,4 +1,4 @@
package com.dfsek.terra.api.lang;
package com.dfsek.terra.api.language;
import com.dfsek.tectonic.config.Configuration;
@@ -1,4 +1,4 @@
package com.dfsek.terra.api.lang;
package com.dfsek.terra.api.language;
import com.dfsek.terra.api.platform.CommandSender;
@@ -1,4 +1,4 @@
package com.dfsek.terra.api.lang;
package com.dfsek.terra.api.language;
import com.dfsek.terra.api.platform.CommandSender;
@@ -1,4 +1,4 @@
package com.dfsek.terra.api.lang;
package com.dfsek.terra.api.language;
import com.dfsek.terra.api.platform.CommandSender;
@@ -0,0 +1,73 @@
package com.dfsek.terra.api.math;
import com.dfsek.terra.api.math.vector.Vector3;
import com.dfsek.terra.api.util.FastRandom;
import com.dfsek.terra.api.util.GlueList;
import java.util.List;
import java.util.Random;
/**
* Class to procedurally determine the spawn point of an object based on a grid with padding between cells.
*/
public class GridSpawn {
private final int separation;
private final int width;
private final int salt;
public GridSpawn(int width, int separation, int salt) {
this.separation = separation;
this.width = width;
this.salt = salt;
}
/**
* Get nearest spawn point
*
* @param x X coordinate
* @param z Z coordinate
* @param seed Seed for RNG
* @return Vector representing nearest spawnpoint
*/
public Vector3 getNearestSpawn(int x, int z, long seed) {
int structureChunkX = x / (width + 2 * separation);
int structureChunkZ = z / (width + 2 * separation);
List<Vector3> zones = new GlueList<>();
for(int xi = structureChunkX - 1; xi <= structureChunkX + 1; xi++) {
for(int zi = structureChunkZ - 1; zi <= structureChunkZ + 1; zi++) {
zones.add(getChunkSpawn(xi, zi, seed));
}
}
Vector3 shortest = zones.get(0);
Vector3 compare = new Vector3(x, 0, z);
for(Vector3 v : zones) {
if(compare.distanceSquared(shortest) > compare.distanceSquared(v)) shortest = v.clone();
}
return shortest;
}
/**
* Get the X/Z coordinates of the spawn point in the nearest Chunk (not Minecraft chunk)
*
* @param structureChunkX Chunk X coordinate
* @param structureChunkZ Chunk Z coordinate
* @param seed Seed for RNG
* @return Vector representing spawnpoint
*/
public Vector3 getChunkSpawn(int structureChunkX, int structureChunkZ, long seed) {
Random r = new FastRandom(MathUtil.getCarverChunkSeed(structureChunkX, structureChunkZ, seed + salt));
int offsetX = r.nextInt(width);
int offsetZ = r.nextInt(width);
int sx = structureChunkX * (width + 2 * separation) + offsetX;
int sz = structureChunkZ * (width + 2 * separation) + offsetZ;
return new Vector3(sx, 0, sz);
}
public int getWidth() {
return width;
}
public int getSeparation() {
return separation;
}
}
@@ -1,6 +1,7 @@
package com.dfsek.terra.api.math;
import com.dfsek.terra.api.util.FastRandom;
import com.dfsek.terra.generation.math.Sampler;
import net.jafama.FastMath;
import java.util.Random;
@@ -9,7 +10,14 @@ import java.util.Random;
* Utility class for mathematical functions.
*/
public final class MathUtil {
private static final double EPSILON = 0.1E-5;
/**
* Epsilon for fuzzy floating point comparisons.
*/
public static final double EPSILON = 1.0E-5;
/**
* Derivative constant.
*/
private static final double DERIVATIVE_DIST = 0.55;
/**
* Gets the standard deviation of an array of doubles.
@@ -68,4 +76,17 @@ public final class MathUtil {
public static boolean equals(double a, double b) {
return a == b || FastMath.abs(a - b) < EPSILON;
}
public static double derivative(Sampler sampler, double x, double y, double z) {
double baseSample = sampler.sample(x, y, z);
double xVal1 = (sampler.sample(x + DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double xVal2 = (sampler.sample(x - DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double zVal1 = (sampler.sample(x, y, z + DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double zVal2 = (sampler.sample(x, y, z - DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double yVal1 = (sampler.sample(x, y + DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
double yVal2 = (sampler.sample(x, y - DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
return Math.sqrt(((xVal2 - xVal1) * (xVal2 - xVal1)) + ((zVal2 - zVal1) * (zVal2 - zVal1)) + ((yVal2 - yVal1) * (yVal2 - yVal1)));
}
}
@@ -1,5 +1,6 @@
package com.dfsek.terra.api.math;
import com.dfsek.terra.api.math.noise.FastNoiseLite;
import com.dfsek.terra.api.world.biome.NormalizationUtil;
import java.util.HashSet;
@@ -1,4 +1,4 @@
package com.dfsek.terra.api.math;// MIT License
package com.dfsek.terra.api.math.noise;// MIT License
import net.jafama.FastMath;
@@ -58,6 +58,7 @@ perform a file-wide replace on the following strings (including / * FNLdouble *
/ *FNLdouble* / double
*/
@SuppressWarnings("ManualMinMaxCalculation")
public class FastNoiseLite {
private static final double[] Gradients2D = {
0.130526192220052f, 0.99144486137381f, 0.38268343236509f, 0.923879532511287f, 0.608761429008721f, 0.793353340291235f,
@@ -328,14 +329,14 @@ public class FastNoiseLite {
private TransformType3D mWarpTransformType3D = TransformType3D.DefaultOpenSimplex2;
private double mDomainWarpAmp = 1.0f;
private static final com.dfsek.terra.api.math.FastNoiseLite CELLULAR_LOOKUP_DEFAULT = new com.dfsek.terra.api.math.FastNoiseLite();
private com.dfsek.terra.api.math.FastNoiseLite cellularNoiseLookup = CELLULAR_LOOKUP_DEFAULT;
private static final FastNoiseLite CELLULAR_LOOKUP_DEFAULT = new FastNoiseLite();
private FastNoiseLite cellularNoiseLookup = CELLULAR_LOOKUP_DEFAULT;
public com.dfsek.terra.api.math.FastNoiseLite getCellularNoiseLookup() {
public FastNoiseLite getCellularNoiseLookup() {
return cellularNoiseLookup;
}
public void setCellularNoiseLookup(com.dfsek.terra.api.math.FastNoiseLite cellularNoiseLookup) {
public void setCellularNoiseLookup(FastNoiseLite cellularNoiseLookup) {
this.cellularNoiseLookup = cellularNoiseLookup;
}
@@ -1442,7 +1443,6 @@ public class FastNoiseLite {
if (mCellularDistanceFunction == CellularDistanceFunction.Euclidean && mCellularReturnType != CellularReturnType.CellValue) {
distance0 = FastSqrt(distance0);
//noinspection ConstantConditions
if (mCellularReturnType != CellularReturnType.CellValue) {
distance1 = FastSqrt(distance1);
}
@@ -1593,8 +1593,6 @@ public class FastNoiseLite {
if (mCellularDistanceFunction == CellularDistanceFunction.Euclidean && mCellularReturnType != CellularReturnType.CellValue) {
distance0 = FastSqrt(distance0);
//noinspection ConstantConditions
if (mCellularReturnType != CellularReturnType.CellValue) {
distance1 = FastSqrt(distance1);
}
@@ -0,0 +1,6 @@
package com.dfsek.terra.api.math.noise;
import parsii.eval.Function;
public interface NoiseFunction extends Function {
}
@@ -0,0 +1,62 @@
package com.dfsek.terra.api.math.noise;
import com.dfsek.terra.generation.config.NoiseBuilder;
import com.dfsek.terra.util.hash.HashMapDoubleDouble;
import parsii.eval.Expression;
import java.util.List;
public class NoiseFunction2 implements NoiseFunction {
private final FastNoiseLite gen;
private final Cache cache = new Cache();
public NoiseFunction2(long seed, NoiseBuilder builder) {
this.gen = builder.build((int) seed);
}
@Override
public int getNumberOfArguments() {
return 2;
}
@Override
public double eval(List<Expression> list) {
return cache.get(gen, list.get(0).evaluate(), list.get(1).evaluate());
}
/**
* Evaluate without cache. For testing.
*
* @param list Parameters.
* @return Result.
*/
public double evalNoCache(List<Expression> list) {
return gen.getNoise(list.get(0).evaluate(), list.get(1).evaluate());
}
@Override
public boolean isNaturalFunction() {
return true;
}
private static class Cache extends HashMapDoubleDouble {
private static final long serialVersionUID = 8915092734723467010L;
private static final int cacheSize = 384;
public double get(FastNoiseLite noise, double x, double z) {
double xx = x >= 0 ? x * 2 : x * -2 - 1;
double zz = z >= 0 ? z * 2 : z * -2 - 1;
double key = (xx >= zz) ? (xx * xx + xx + zz) : (zz * zz + xx);
double value = this.get(key);
if(this.size() > cacheSize) {
this.clear();
}
return (value == 4.9E-324D ? addAndReturn(noise.getNoise(x, z), key) : value);
}
private double addAndReturn(double value, double key) {
this.put(key, value);
return value;
}
}
}
@@ -0,0 +1,29 @@
package com.dfsek.terra.api.math.noise;
import com.dfsek.terra.generation.config.NoiseBuilder;
import parsii.eval.Expression;
import java.util.List;
public class NoiseFunction3 implements NoiseFunction {
private final FastNoiseLite gen;
public NoiseFunction3(long seed, NoiseBuilder builder) {
this.gen = builder.build((int) seed);
}
@Override
public int getNumberOfArguments() {
return 3;
}
@Override
public double eval(List<Expression> list) {
return gen.getNoise(list.get(0).evaluate(), list.get(1).evaluate(), list.get(2).evaluate());
}
@Override
public boolean isNaturalFunction() {
return true;
}
}
@@ -0,0 +1,29 @@
package com.dfsek.terra.api.math.parsii;
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<Expression> list) {
return 0;
}
@Override
public boolean isNaturalFunction() {
return true;
}
}
@@ -0,0 +1,30 @@
package com.dfsek.terra.api.math.parsii;
import com.dfsek.terra.api.util.FastRandom;
import parsii.eval.Expression;
import parsii.eval.Function;
import java.util.List;
/**
* Provides access to a PRNG ({@link com.dfsek.terra.api.util.FastRandom})
* <p>
* Takes 1 argument, which sets the seed
*/
public class RandomFunction implements Function {
@Override
public int getNumberOfArguments() {
return 1;
}
@Override
public double eval(List<Expression> list) {
long seed = (long) list.get(0).evaluate();
return new FastRandom(seed).nextDouble();
}
@Override
public boolean isNaturalFunction() {
return true;
}
}
@@ -0,0 +1,7 @@
package com.dfsek.terra.api.math.pixel;
public class Distribution {
public Distribution(Rectangle bound, int numPoints, double minRad) {
}
}
@@ -0,0 +1,10 @@
package com.dfsek.terra.api.math.pixel;
import com.dfsek.terra.api.math.vector.Vector2;
import java.util.Set;
@SuppressWarnings("unused")
public abstract class Polygon {
public abstract Set<Vector2> getContainedPixels();
}
@@ -0,0 +1,35 @@
package com.dfsek.terra.api.math.pixel;
import com.dfsek.terra.api.math.vector.Vector2;
import net.jafama.FastMath;
import java.util.HashSet;
import java.util.Set;
@SuppressWarnings("unused")
public class Rectangle extends Polygon {
private final Vector2 min;
private final Vector2 max;
public Rectangle(Vector2 min, Vector2 max) {
this.max = new Vector2(FastMath.min(min.getX(), max.getX()), FastMath.min(min.getZ(), max.getZ()));
this.min = new Vector2(FastMath.max(min.getX(), max.getX()), FastMath.max(min.getZ(), max.getZ()));
}
public Rectangle(Vector2 center, double xRadius, double zRadius) {
Vector2 rad = new Vector2(xRadius, zRadius);
this.min = center.clone().subtract(rad);
this.max = center.clone().add(rad);
}
@Override
public Set<Vector2> getContainedPixels() {
Set<Vector2> pixels = new HashSet<>();
for(int x = (int) min.getX(); x <= max.getX(); x++) {
for(int z = (int) min.getZ(); z <= max.getZ(); z++) {
pixels.add(new Vector2(x, z));
}
}
return pixels;
}
}
@@ -163,6 +163,6 @@ public class Location implements Cloneable {
@Override
public String toString() {
return "(" + getX() + ", " + getY() + ", " + getZ() + ")";
return "[" + world + ": (" + getX() + ", " + getY() + ", " + getZ() + ")]";
}
}
@@ -1,5 +1,6 @@
package com.dfsek.terra.api.math.vector;
import com.dfsek.terra.api.math.MathUtil;
import net.jafama.FastMath;
/**
@@ -157,11 +158,9 @@ public class Vector2 implements Cloneable {
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Vector2)) {
return false;
}
if(!(obj instanceof Vector2)) return false;
Vector2 other = (Vector2) obj;
return other.x == this.x && other.z == this.z;
return MathUtil.equals(this.x, other.x) && MathUtil.equals(this.z, other.z);
}
@Override
@@ -1,15 +1,12 @@
package com.dfsek.terra.api.math.vector;
import com.dfsek.terra.api.math.MathUtil;
import com.dfsek.terra.api.platform.world.World;
import net.jafama.FastMath;
import org.jetbrains.annotations.NotNull;
public class Vector3 implements Cloneable {
/**
* Threshold for fuzzy equals().
*/
private static final double epsilon = 0.000001;
private double x;
private double y;
@@ -21,15 +18,6 @@ public class Vector3 implements Cloneable {
this.z = z;
}
/**
* Get the threshold used for equals().
*
* @return The epsilon.
*/
public static double getEpsilon() {
return epsilon;
}
public double getZ() {
return z;
}
@@ -119,7 +107,7 @@ public class Vector3 implements Cloneable {
* @return whether the vector is normalised
*/
public boolean isNormalized() {
return Math.abs(this.lengthSquared() - 1) < getEpsilon();
return MathUtil.equals(this.lengthSquared(), 1);
}
/**
@@ -333,12 +321,14 @@ public class Vector3 implements Cloneable {
*/
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Vector3)) {
return false;
}
if(!(obj instanceof Vector3)) return false;
Vector3 other = (Vector3) obj;
return Math.abs(x - other.x) < epsilon && Math.abs(y - other.y) < epsilon && Math.abs(z - other.z) < epsilon && (this.getClass().equals(obj.getClass()));
return MathUtil.equals(x, other.x) && MathUtil.equals(y, other.y) && MathUtil.equals(x, other.z);
}
@Override
public String toString() {
return "(" + getX() + ", " + getY() + ", " + getZ() + ")";
}
}
@@ -0,0 +1,10 @@
package com.dfsek.terra.api.math.voxel;
import com.dfsek.terra.api.math.vector.Vector3;
public class Cylinder extends VoxelGeometry {
public Cylinder(Vector3 start, int rad, int height) {
}
}
@@ -0,0 +1,19 @@
package com.dfsek.terra.api.math.voxel;
import com.dfsek.terra.api.math.noise.FastNoiseLite;
import com.dfsek.terra.api.math.vector.Vector3;
public class DeformedSphere extends VoxelGeometry {
public DeformedSphere(Vector3 start, int rad, double deform, FastNoiseLite noise) {
for(int x = -rad; x <= rad; x++) {
for(int y = -rad; y <= rad; y++) {
for(int z = -rad; z <= rad; z++) {
Vector3 c = new Vector3(x, y, z);
if(c.length() < (rad + 0.5) * ((noise.getNoise(x, y, z) + 1) * deform)) {
addVector(c.add(start));
}
}
}
}
}
}
@@ -0,0 +1,18 @@
package com.dfsek.terra.api.math.voxel;
import com.dfsek.terra.api.math.vector.Vector3;
public class Sphere extends VoxelGeometry {
public Sphere(Vector3 start, int rad) {
for(int x = -rad; x <= rad; x++) {
for(int y = -rad; y <= rad; y++) {
for(int z = -rad; z <= rad; z++) {
Vector3 c = new Vector3(x, y, z);
if(c.length() < rad + 0.5) {
addVector(c.add(start));
}
}
}
}
}
}
@@ -0,0 +1,15 @@
package com.dfsek.terra.api.math.voxel;
import com.dfsek.terra.api.math.vector.Vector3;
public class Tube extends VoxelGeometry {
public Tube(Vector3 start, Vector3 end, int radius) {
Vector3 step = start.clone().subtract(end).normalize();
Vector3 run = start.clone();
int steps = (int) start.distance(end);
for(int i = 0; i < steps; i++) {
merge(new Sphere(run, radius));
run.add(step);
}
}
}
@@ -0,0 +1,27 @@
package com.dfsek.terra.api.math.voxel;
import com.dfsek.terra.api.math.vector.Vector3;
import com.dfsek.terra.api.util.GlueList;
import java.util.List;
public abstract class VoxelGeometry {
private final List<Vector3> geometry = new GlueList<>();
public static VoxelGeometry getBlank() {
return new VoxelGeometry() {
};
}
public List<Vector3> getGeometry() {
return geometry;
}
protected void addVector(Vector3 v) {
geometry.add(v);
}
public void merge(VoxelGeometry other) {
geometry.addAll(other.geometry);
}
}
@@ -2,7 +2,7 @@ package com.dfsek.terra.api.platform;
import com.dfsek.terra.TerraWorld;
import com.dfsek.terra.api.LoaderRegistrar;
import com.dfsek.terra.api.lang.Language;
import com.dfsek.terra.api.language.Language;
import com.dfsek.terra.api.platform.handle.ItemHandle;
import com.dfsek.terra.api.platform.handle.WorldHandle;
import com.dfsek.terra.api.platform.world.World;
@@ -15,10 +15,7 @@ public class WorldProfiler {
public WorldProfiler(World w) {
if(w.getGenerator().getTerraGenerator() == null)
throw new IllegalArgumentException("Attempted to instantiate profiler on non-Gaea managed world!");
this.addMeasurement(new Measurement(2500000, DataType.PERIOD_MILLISECONDS), "TotalChunkGenTime")
.addMeasurement(new Measurement(2500000, DataType.PERIOD_MILLISECONDS), "ChunkBaseGenTime")
.addMeasurement(new Measurement(2000000, DataType.PERIOD_MILLISECONDS), "BiomeApplyTime")
.addMeasurement(new Measurement(2000000, DataType.PERIOD_MILLISECONDS), "PopulationManagerTime");
this.addMeasurement(new Measurement(2500000, DataType.PERIOD_MILLISECONDS), "TotalChunkGenTime");
isProfiling = false;
this.world = w;
}
@@ -34,6 +34,7 @@ import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
@@ -42,7 +43,7 @@ public class StructureScript {
private final Block block;
private final String id;
String tempID;
private final LinkedHashMap<Location, StructureBuffer> cache;
private final Map<Location, StructureBuffer> cache;
public StructureScript(InputStream inputStream, TerraPlugin main, ScriptRegistry registry, LootRegistry lootRegistry, SamplerCache cache) throws ParseException {
Parser parser;
@@ -79,12 +80,12 @@ public class StructureScript {
block = parser.parse();
this.id = parser.getID();
tempID = id;
this.cache = new LinkedHashMap<Location, StructureBuffer>() {
this.cache = Collections.synchronizedMap(new LinkedHashMap<Location, StructureBuffer>() {
@Override
protected boolean removeEldestEntry(Map.Entry<Location, StructureBuffer> eldest) {
return this.size() > main.getTerraConfig().getStructureCache();
}
};
});
}
/**
@@ -113,12 +114,14 @@ public class StructureScript {
}
private StructureBuffer computeBuffer(Location location, Random random, Rotation rotation) {
return cache.computeIfAbsent(location, loc -> {
StructureBuffer buf = new StructureBuffer(loc);
Block.ReturnInfo<?> level = block.apply(new TerraImplementationArguments(buf, rotation, random, 0));
buf.setSucceeded(!level.getLevel().equals(Block.ReturnLevel.FAIL));
return buf;
});
synchronized(cache) {
return cache.computeIfAbsent(location, loc -> {
StructureBuffer buf = new StructureBuffer(loc);
Block.ReturnInfo<?> level = block.apply(new TerraImplementationArguments(buf, rotation, random, 0));
buf.setSucceeded(!level.getLevel().equals(Block.ReturnLevel.FAIL));
return buf;
});
}
}
public boolean executeInBuffer(Buffer buffer, Random random, Rotation rotation, int recursions) {
@@ -1,6 +1,6 @@
package com.dfsek.terra.api.world.biome;
import com.dfsek.terra.api.math.FastNoiseLite;
import com.dfsek.terra.api.math.noise.FastNoiseLite;
import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.world.generation.GenerationPhase;
@@ -1,7 +1,7 @@
package com.dfsek.terra.api.world.palette;
import com.dfsek.terra.api.math.FastNoiseLite;
import com.dfsek.terra.api.math.ProbabilityCollection;
import com.dfsek.terra.api.math.noise.FastNoiseLite;
import com.dfsek.terra.api.util.GlueList;
import java.util.List;
@@ -1,6 +1,6 @@
package com.dfsek.terra.api.world.palette;
import com.dfsek.terra.api.math.FastNoiseLite;
import com.dfsek.terra.api.math.noise.FastNoiseLite;
import java.util.List;