From 16b0ea2c13a33b1c053af4c520dca3c1f493eded Mon Sep 17 00:00:00 2001 From: dfsek Date: Sun, 17 Oct 2021 14:18:48 -0700 Subject: [PATCH] Initial commit --- .../addons/chunk-generator-noise-3d/README.md | 4 + .../chunk-generator-noise-3d/build.gradle.kts | 2 + .../chunkgenerator/BiomePaletteTemplate.java | 30 +++ .../NoiseChunkGenerator3DAddon.java | 53 +++++ .../addons/chunkgenerator/PaletteUtil.java | 23 +++ .../generators/NoiseChunkGenerator3D.java | 193 ++++++++++++++++++ .../interpolation/ChunkInterpolator2D.java | 97 +++++++++ .../interpolation/ChunkInterpolator3D.java | 118 +++++++++++ .../interpolation/ElevationInterpolator.java | 45 ++++ .../math/interpolation/Interpolator.java | 50 +++++ .../math/interpolation/Interpolator3.java | 32 +++ .../generation/math/samplers/Sampler3D.java | 32 +++ .../chunkgenerator/palette/PaletteHolder.java | 23 +++ .../palette/PaletteHolderBuilder.java | 38 ++++ .../palette/PaletteHolderLoader.java | 27 +++ .../chunkgenerator/palette/PaletteInfo.java | 36 ++++ .../chunkgenerator/palette/SlantHolder.java | 23 +++ .../palette/SlantHolderLoader.java | 29 +++ 18 files changed, 855 insertions(+) create mode 100644 common/addons/chunk-generator-noise-3d/README.md create mode 100644 common/addons/chunk-generator-noise-3d/build.gradle.kts create mode 100644 common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/BiomePaletteTemplate.java create mode 100644 common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/NoiseChunkGenerator3DAddon.java create mode 100644 common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/PaletteUtil.java create mode 100644 common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/generators/NoiseChunkGenerator3D.java create mode 100644 common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/ChunkInterpolator2D.java create mode 100644 common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/ChunkInterpolator3D.java create mode 100644 common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/ElevationInterpolator.java create mode 100644 common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/Interpolator.java create mode 100644 common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/Interpolator3.java create mode 100644 common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/samplers/Sampler3D.java create mode 100644 common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/PaletteHolder.java create mode 100644 common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/PaletteHolderBuilder.java create mode 100644 common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/PaletteHolderLoader.java create mode 100644 common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/PaletteInfo.java create mode 100644 common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/SlantHolder.java create mode 100644 common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/SlantHolderLoader.java diff --git a/common/addons/chunk-generator-noise-3d/README.md b/common/addons/chunk-generator-noise-3d/README.md new file mode 100644 index 000000000..b235df2e6 --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/README.md @@ -0,0 +1,4 @@ +# chunk-generator-noise-3d + +Registers the `NOISE_3D` chunk generator, a chunk generator which uses biomes' +samplers in 3D to generate chunk data. \ No newline at end of file diff --git a/common/addons/chunk-generator-noise-3d/build.gradle.kts b/common/addons/chunk-generator-noise-3d/build.gradle.kts new file mode 100644 index 000000000..7d82dc72f --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/build.gradle.kts @@ -0,0 +1,2 @@ +dependencies { +} diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/BiomePaletteTemplate.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/BiomePaletteTemplate.java new file mode 100644 index 000000000..037bcde3f --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/BiomePaletteTemplate.java @@ -0,0 +1,30 @@ +package com.dfsek.terra.addons.chunkgenerator; + +import com.dfsek.tectonic.annotations.Default; +import com.dfsek.tectonic.annotations.Value; +import com.dfsek.tectonic.loading.object.ObjectTemplate; + +import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolder; +import com.dfsek.terra.addons.chunkgenerator.palette.PaletteInfo; +import com.dfsek.terra.addons.chunkgenerator.palette.SlantHolder; +import com.dfsek.terra.api.config.meta.Meta; +import com.dfsek.terra.api.world.generator.Palette; + + +public class BiomePaletteTemplate implements ObjectTemplate { + @Value("slant") + @Default + private @Meta SlantHolder slant; + @Value("palette") + private @Meta PaletteHolder palette; + @Value("ocean.level") + private @Meta int seaLevel; + + @Value("ocean.palette") + private @Meta Palette oceanPalette; + + @Override + public PaletteInfo get() { + return new PaletteInfo(palette, slant, oceanPalette, seaLevel); + } +} diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/NoiseChunkGenerator3DAddon.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/NoiseChunkGenerator3DAddon.java new file mode 100644 index 000000000..a488049cb --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/NoiseChunkGenerator3DAddon.java @@ -0,0 +1,53 @@ +package com.dfsek.terra.addons.chunkgenerator; + +import com.dfsek.terra.addons.chunkgenerator.generation.generators.NoiseChunkGenerator3D; +import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolder; +import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolderLoader; +import com.dfsek.terra.addons.chunkgenerator.palette.SlantHolder; +import com.dfsek.terra.addons.chunkgenerator.palette.SlantHolderLoader; +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.addon.TerraAddon; +import com.dfsek.terra.api.addon.annotations.Addon; +import com.dfsek.terra.api.addon.annotations.Author; +import com.dfsek.terra.api.addon.annotations.Version; +import com.dfsek.terra.api.event.events.config.ConfigurationLoadEvent; +import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent; +import com.dfsek.terra.api.event.functional.FunctionalEventHandler; +import com.dfsek.terra.api.inject.annotations.Inject; +import com.dfsek.terra.api.world.biome.TerraBiome; +import com.dfsek.terra.api.world.generator.ChunkGeneratorProvider; + + +@Addon("chunk-generator-noise-3d") +@Author("Terra") +@Version("1.0.0") +public class NoiseChunkGenerator3DAddon extends TerraAddon { + @Inject + private Platform platform; + + @Override + public void initialize() { + platform.getEventManager() + .getHandler(FunctionalEventHandler.class) + .register(this, ConfigPackPreLoadEvent.class) + .then(event -> { + event.getPack().getOrCreateRegistry(ChunkGeneratorProvider.class).register("NOISE_3D", + pack -> new NoiseChunkGenerator3D(pack, + platform)); + event.getPack() + .applyLoader(SlantHolder.class, new SlantHolderLoader()) + .applyLoader(PaletteHolder.class, new PaletteHolderLoader()); + }) + .failThrough(); + + platform.getEventManager() + .getHandler(FunctionalEventHandler.class) + .register(this, ConfigurationLoadEvent.class) + .then(event -> { + if(event.is(TerraBiome.class)) { + event.getLoadedObject(TerraBiome.class).getContext().put(event.load(new BiomePaletteTemplate()).get()); + } + }) + .failThrough(); + } +} diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/PaletteUtil.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/PaletteUtil.java new file mode 100644 index 000000000..b7901d716 --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/PaletteUtil.java @@ -0,0 +1,23 @@ +package com.dfsek.terra.addons.chunkgenerator; + +import com.dfsek.terra.addons.chunkgenerator.palette.PaletteInfo; +import com.dfsek.terra.addons.chunkgenerator.palette.SlantHolder; +import com.dfsek.terra.api.util.MathUtil; +import com.dfsek.terra.api.world.biome.GenerationSettings; +import com.dfsek.terra.api.world.generator.Palette; +import com.dfsek.terra.api.util.math.Sampler; + + +public final class PaletteUtil { + public static Palette getPalette(int x, int y, int z, GenerationSettings c, Sampler sampler, PaletteInfo paletteInfo) { + SlantHolder slant = paletteInfo.getSlantHolder(); + if(slant != null) { + double slope = MathUtil.derivative(sampler, x, y, z); + if(slope > slant.getMinSlope()) { + return slant.getPalette(slope).getPalette(y); + } + } + + return paletteInfo.getPaletteHolder().getPalette(y); + } +} \ No newline at end of file diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/generators/NoiseChunkGenerator3D.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/generators/NoiseChunkGenerator3D.java new file mode 100644 index 000000000..400e51acd --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/generators/NoiseChunkGenerator3D.java @@ -0,0 +1,193 @@ +package com.dfsek.terra.addons.chunkgenerator.generation.generators; + +import com.dfsek.terra.api.Platform; + +import net.jafama.FastMath; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import com.dfsek.terra.addons.chunkgenerator.PaletteUtil; +import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.Sampler3D; +import com.dfsek.terra.addons.chunkgenerator.palette.PaletteInfo; +import com.dfsek.terra.api.block.state.BlockState; +import com.dfsek.terra.api.block.state.properties.base.Properties; +import com.dfsek.terra.api.block.state.properties.enums.Direction; +import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.api.profiler.ProfileFrame; +import com.dfsek.terra.api.util.vector.Vector3; +import com.dfsek.terra.api.world.BiomeGrid; +import com.dfsek.terra.api.world.World; +import com.dfsek.terra.api.world.biome.GenerationSettings; +import com.dfsek.terra.api.world.biome.TerraBiome; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; +import com.dfsek.terra.api.world.generator.ChunkData; +import com.dfsek.terra.api.world.generator.ChunkGenerator; +import com.dfsek.terra.api.world.generator.GenerationStage; +import com.dfsek.terra.api.world.generator.Palette; +import com.dfsek.terra.api.util.math.Sampler; + + +public class NoiseChunkGenerator3D implements ChunkGenerator { + private final ConfigPack configPack; + private final Platform platform; + private final List generationStages = new ArrayList<>(); + + private final BlockState air; + + public NoiseChunkGenerator3D(ConfigPack c, Platform platform) { + this.configPack = c; + this.platform = platform; + this.air = platform.getWorldHandle().air(); + c.getStages().forEach(stage -> generationStages.add(stage.newInstance(c))); + } + + @SuppressWarnings("try") + static void biomes(@NotNull World world, int chunkX, int chunkZ, @NotNull BiomeGrid biome, Platform platform) { + try(ProfileFrame ignore = platform.getProfiler().profile("biomes")) { + int xOrig = (chunkX << 4); + int zOrig = (chunkZ << 4); + long seed = world.getSeed(); + BiomeProvider grid = world.getBiomeProvider(); + for(int x = 0; x < 4; x++) { + for(int z = 0; z < 4; z++) { + int cx = xOrig + (x << 2); + int cz = zOrig + (z << 2); + TerraBiome b = grid.getBiome(cx, cz, seed); + + biome.setBiome(cx, cz, b.getVanillaBiomes().get(b.getGenerator().getBiomeNoise(), cx, 0, cz, world.getSeed())); + } + } + } + } + + @Override + @SuppressWarnings("try") + public ChunkData generateChunkData(@NotNull World world, Random random, int chunkX, int chunkZ, ChunkData chunk) { + try(ProfileFrame ignore = platform.getProfiler().profile("chunk_base_3d")) { + BiomeProvider grid = world.getBiomeProvider(); + + int xOrig = (chunkX << 4); + int zOrig = (chunkZ << 4); + + Sampler sampler = world.getConfig().getSamplerCache().getChunk(chunkX, chunkZ); + + long seed = world.getSeed(); + + for(int x = 0; x < 16; x++) { + for(int z = 0; z < 16; z++) { + int paletteLevel = 0; + + int cx = xOrig + x; + int cz = zOrig + z; + + TerraBiome biome = grid.getBiome(cx, cz, seed); + + PaletteInfo paletteInfo = biome.getContext().get(PaletteInfo.class); + + if(paletteInfo == null) { + platform.logger().info("null palette: " + biome.getID()); + } + + GenerationSettings generationSettings = biome.getGenerator(); + + int sea = paletteInfo.getSeaLevel(); + Palette seaPalette = paletteInfo.getOcean(); + + boolean justSet = false; + BlockState data = null; + for(int y = world.getMaxHeight() - 1; y >= world.getMinHeight(); y--) { + if(sampler.sample(x, y, z) > 0) { + justSet = true; + + data = PaletteUtil.getPalette(x, y, z, generationSettings, sampler, paletteInfo).get(paletteLevel, cx, y, cz, + seed); + chunk.setBlock(x, y, z, data); + + paletteLevel++; + } else if(y <= sea) { + chunk.setBlock(x, y, z, seaPalette.get(sea - y, x + xOrig, y, z + zOrig, seed)); + + justSet = false; + paletteLevel = 0; + } else { + + justSet = false; + paletteLevel = 0; + } + } + } + } + return chunk; + } + } + + @Override + public void generateBiomes(@NotNull World world, @NotNull Random random, int chunkX, int chunkZ, @NotNull BiomeGrid biome) { + biomes(world, chunkX, chunkZ, biome, platform); + } + + @Override + public Sampler createSampler(int chunkX, int chunkZ, BiomeProvider provider, World world, int elevationSmooth) { + return new Sampler3D(chunkX, chunkZ, provider, world, elevationSmooth); + } + + @Override + public ConfigPack getConfigPack() { + return configPack; + } + + @Override + public Platform getPlatform() { + return platform; + } + + @Override + public List getGenerationStages() { + return generationStages; + } + + @Override + public BlockState getBlock(World world, int x, int y, int z) { + BiomeProvider provider = world.getBiomeProvider(); + TerraBiome biome = provider.getBiome(x, z, world.getSeed()); + Sampler sampler = world.getConfig().getSamplerCache().get(x, z); + + PaletteInfo paletteInfo = biome.getContext().get(PaletteInfo.class); + Palette palette = PaletteUtil.getPalette(x, y, z, biome.getGenerator(), sampler, paletteInfo); + int fdX = FastMath.floorMod(x, 16); + int fdZ = FastMath.floorMod(z, 16); + double noise = sampler.sample(fdX, y, fdZ); + if(noise > 0) { + int level = 0; + for(int yi = world.getMaxHeight() - 1; yi > y; yi--) { + if(sampler.sample(fdX, yi, fdZ) > 0) level++; + else level = 0; + } + return palette.get(level, x, y, z, world.getSeed()); + } else if(y <= paletteInfo.getSeaLevel()) { + return paletteInfo.getOcean().get(paletteInfo.getSeaLevel() - y, x, y, z, world.getSeed()); + } else return air; + } + + private boolean placeStair(BlockState orig, ChunkData chunk, Vector3 block, double thresh, Sampler sampler, BlockState stairNew) { + + if(sampler.sample(block.getBlockX() - 0.55, block.getY(), block.getZ()) > thresh) { + stairNew.set(Properties.DIRECTION, Direction.WEST); + } else if(sampler.sample(block.getBlockX(), block.getY(), block.getZ() - 0.55) > thresh) { + stairNew.set(Properties.DIRECTION, Direction.NORTH); + } else if(sampler.sample(block.getBlockX(), block.getY(), block.getZ() + 0.55) > thresh) { + stairNew.set(Properties.DIRECTION, Direction.SOUTH); + } else if(sampler.sample(block.getX() + 0.55, block.getY(), block.getZ()) > thresh) { + stairNew.set(Properties.DIRECTION, Direction.EAST); + } else stairNew = null; + if(stairNew != null) { + stairNew.setIfPresent(Properties.WATERLOGGED, orig.getBlockType().isWater()); + chunk.setBlock(block.getBlockX(), block.getBlockY(), block.getBlockZ(), stairNew); + return true; + } + return false; + } +} diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/ChunkInterpolator2D.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/ChunkInterpolator2D.java new file mode 100644 index 000000000..fb02692fd --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/ChunkInterpolator2D.java @@ -0,0 +1,97 @@ +package com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation; + +import net.jafama.FastMath; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiFunction; + +import com.dfsek.terra.api.util.mutable.MutableInteger; +import com.dfsek.terra.api.util.vector.Vector3; +import com.dfsek.terra.api.world.World; +import com.dfsek.terra.api.world.biome.GenerationSettings; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; +import com.dfsek.terra.api.world.generator.ChunkInterpolator; + + +/** + * Class to abstract away the Interpolators needed to generate a chunk.
+ * Contains method to get interpolated noise at a coordinate within the chunk. + */ +public class ChunkInterpolator2D implements ChunkInterpolator { + private final Interpolator[][] interpGrid = new Interpolator[4][4]; + private final BiFunction noiseGetter; + + /** + * Instantiates a 3D ChunkInterpolator3D at a pair of chunk coordinates. + * + * @param chunkX X coordinate of the chunk. + * @param chunkZ Z coordinate of the chunk. + * @param provider Biome Provider to use for biome fetching. + */ + public ChunkInterpolator2D(World w, int chunkX, int chunkZ, BiomeProvider provider, + BiFunction noiseGetter) { + this.noiseGetter = noiseGetter; + int xOrigin = chunkX << 4; + int zOrigin = chunkZ << 4; + + long seed = w.getSeed(); + + double[][] noiseStorage = new double[5][5]; + + for(int x = 0; x < 5; x++) { + for(int z = 0; z < 5; z++) { + GenerationSettings generationSettings = provider.getBiome(xOrigin + (x << 2), zOrigin + (z << 2), seed).getGenerator(); + Map genMap = new HashMap<>(); + + int step = generationSettings.getBlendStep(); + int blend = generationSettings.getBlendDistance(); + + for(int xi = -blend; xi <= blend; xi++) { + for(int zi = -blend; zi <= blend; zi++) { + genMap.computeIfAbsent( + provider.getBiome(xOrigin + (x << 2) + (xi * step), zOrigin + (z << 2) + (zi * step), seed).getGenerator(), + g -> new MutableInteger(0)).increment(); // Increment by 1 + } + } + + noiseStorage[x][z] = computeNoise(genMap, (x << 2) + xOrigin, 0, (z << 2) + zOrigin); + } + } + + for(int x = 0; x < 4; x++) { + for(int z = 0; z < 4; z++) { + interpGrid[x][z] = new Interpolator( + noiseStorage[x][z], + noiseStorage[x + 1][z], + noiseStorage[x][z + 1], + noiseStorage[x + 1][z + 1]); + } + } + } + + private static int reRange(int value, int high) { + return FastMath.max(FastMath.min(value, high), 0); + } + + public double computeNoise(GenerationSettings generationSettings, double x, double y, double z) { + return noiseGetter.apply(generationSettings, new Vector3(x, y, z)); + } + + /** + * Gets the noise at a pair of internal chunk coordinates. + * + * @param x The internal X coordinate (0-15). + * @param z The internal Z coordinate (0-15). + * + * @return double - The interpolated noise at the coordinates. + */ + @Override + public double getNoise(double x, double y, double z) { + return interpGrid[reRange(((int) x) / 4, 3)][reRange(((int) z) / 4, 3)].bilerp((x % 4) / 4, (z % 4) / 4); + } + + public double getNoise(int x, int y, int z) { + return interpGrid[x / 4][z / 4].bilerp((double) (x % 4) / 4, (double) (z % 4) / 4); + } +} diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/ChunkInterpolator3D.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/ChunkInterpolator3D.java new file mode 100644 index 000000000..87c96f5f6 --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/ChunkInterpolator3D.java @@ -0,0 +1,118 @@ +package com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation; + +import net.jafama.FastMath; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiFunction; + +import com.dfsek.terra.api.util.mutable.MutableInteger; +import com.dfsek.terra.api.util.vector.Vector3; +import com.dfsek.terra.api.world.World; +import com.dfsek.terra.api.world.biome.GenerationSettings; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; +import com.dfsek.terra.api.world.generator.ChunkInterpolator; + + +/** + * Class to abstract away the Interpolators needed to generate a chunk.
+ * Contains method to get interpolated noise at a coordinate within the chunk. + */ +public class ChunkInterpolator3D implements ChunkInterpolator { + private final Interpolator3[][][] interpGrid; + private final BiFunction noiseGetter; + + private final int min; + private final int max; + + /** + * Instantiates a 3D ChunkInterpolator3D at a pair of chunk coordinates. + * + * @param chunkX X coordinate of the chunk. + * @param chunkZ Z coordinate of the chunk. + * @param provider Biome Provider to use for biome fetching. + */ + public ChunkInterpolator3D(World w, int chunkX, int chunkZ, BiomeProvider provider, + BiFunction noiseGetter) { + this.noiseGetter = noiseGetter; + int xOrigin = chunkX << 4; + int zOrigin = chunkZ << 4; + + this.max = w.getMaxHeight(); + this.min = w.getMinHeight(); + int range = max - min + 1; + + int size = range >> 2; + + interpGrid = new Interpolator3[4][size][4]; + + double[][][] noiseStorage = new double[5][5][size + 1]; + + long seed = w.getSeed(); + + for(int x = 0; x < 5; x++) { + for(int z = 0; z < 5; z++) { + GenerationSettings generationSettings = provider.getBiome(xOrigin + (x << 2), zOrigin + (z << 2), seed).getGenerator(); + Map genMap = new HashMap<>(); + + int step = generationSettings.getBlendStep(); + int blend = generationSettings.getBlendDistance(); + + for(int xi = -blend; xi <= blend; xi++) { + for(int zi = -blend; zi <= blend; zi++) { + genMap.computeIfAbsent( + provider.getBiome(xOrigin + (x << 2) + (xi * step), zOrigin + (z << 2) + (zi * step), seed).getGenerator(), + g -> new MutableInteger(0)).increment(); // Increment by 1 + } + } + + for(int y = 0; y < size + 1; y++) { + noiseStorage[x][z][y] = computeNoise(genMap, (x << 2) + xOrigin, (y << 2) + min, (z << 2) + zOrigin); + } + } + } + + for(int x = 0; x < 4; x++) { + for(int z = 0; z < 4; z++) { + for(int y = 0; y < size; y++) { + interpGrid[x][y][z] = new Interpolator3( + noiseStorage[x][z][y], + noiseStorage[x + 1][z][y], + noiseStorage[x][z][y + 1], + noiseStorage[x + 1][z][y + 1], + noiseStorage[x][z + 1][y], + noiseStorage[x + 1][z + 1][y], + noiseStorage[x][z + 1][y + 1], + noiseStorage[x + 1][z + 1][y + 1]); + } + } + } + } + + private static int reRange(int value, int high) { + return FastMath.max(FastMath.min(value, high), 0); + } + + public double computeNoise(GenerationSettings generationSettings, double x, double y, double z) { + return noiseGetter.apply(generationSettings, new Vector3(x, y, z)); + } + + /** + * Gets the noise at a pair of internal chunk coordinates. + * + * @param x The internal X coordinate (0-15). + * @param z The internal Z coordinate (0-15). + * + * @return double - The interpolated noise at the coordinates. + */ + @Override + public double getNoise(double x, double y, double z) { + return interpGrid[reRange(((int) x) / 4, 3)][(FastMath.max(FastMath.min(((int) y), max), min) - min) / 4][reRange(((int) z) / 4, + 3)].trilerp( + (x % 4) / 4, (y % 4) / 4, (z % 4) / 4); + } + + public double getNoise(int x, int y, int z) { + return interpGrid[x / 4][(y - min) / 4][z / 4].trilerp((double) (x % 4) / 4, (double) (y % 4) / 4, (double) (z % 4) / 4); + } +} diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/ElevationInterpolator.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/ElevationInterpolator.java new file mode 100644 index 000000000..e2b01942e --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/ElevationInterpolator.java @@ -0,0 +1,45 @@ +package com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation; + +import com.dfsek.terra.api.world.World; +import com.dfsek.terra.api.world.biome.GenerationSettings; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; + + +public class ElevationInterpolator { + private final double[][] values = new double[18][18]; + + public ElevationInterpolator(World world, int chunkX, int chunkZ, BiomeProvider provider, int smooth) { + int xOrigin = chunkX << 4; + int zOrigin = chunkZ << 4; + + long seed = world.getSeed(); + + GenerationSettings[][] gens = new GenerationSettings[18 + 2 * smooth][18 + 2 * smooth]; + + // Precompute generators. + for(int x = -1 - smooth; x <= 16 + smooth; x++) { + for(int z = -1 - smooth; z <= 16 + smooth; z++) { + gens[x + 1 + smooth][z + 1 + smooth] = provider.getBiome(xOrigin + x, zOrigin + z, seed).getGenerator(); + } + } + + for(int x = -1; x <= 16; x++) { + for(int z = -1; z <= 16; z++) { + double noise = 0; + double div = 0; + for(int xi = -smooth; xi <= smooth; xi++) { + for(int zi = -smooth; zi <= smooth; zi++) { + GenerationSettings gen = gens[x + 1 + smooth + xi][z + 1 + smooth + zi]; + noise += gen.getElevationSampler().getNoiseSeeded(seed, xOrigin + x, zOrigin + z) * gen.getElevationWeight(); + div += gen.getElevationWeight(); + } + } + values[x + 1][z + 1] = noise / div; + } + } + } + + public double getElevation(int x, int z) { + return values[x + 1][z + 1]; + } +} diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/Interpolator.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/Interpolator.java new file mode 100644 index 000000000..d67ac6c10 --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/Interpolator.java @@ -0,0 +1,50 @@ +package com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation; + +/** + * Class for bilinear interpolation of values arranged on a unit square. + */ +public class Interpolator { + private final double v0, v1, v2, v3; + + /** + * Constructs an interpolator with given values as vertices of a unit square. + * + * @param v0 - (0,0) + * @param v1 - (1,0) + * @param v2 - (0,1) + * @param v3 - (1,1) + */ + public Interpolator(double v0, double v1, double v2, double v3) { + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + } + + /** + * 1D Linear interpolation between 2 points 1 unit apart. + * + * @param t - Distance from v0. Total distance between v0 and v1 is 1 unit. + * @param v0 - Value at v0. + * @param v1 - Value at v1. + * + * @return double - The interpolated value. + */ + public static double lerp(double t, double v0, double v1) { + return v0 + t * (v1 - v0); + } + + /** + * 2D Bilinear interpolation between 4 points on a unit square. + * + * @param s - X value + * @param t - Z value + * + * @return double - The interpolated value. + */ + public double bilerp(double s, double t) { + double v01 = lerp(s, v0, v1); + double v23 = lerp(s, v2, v3); + return lerp(t, v01, v23); + } +} \ No newline at end of file diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/Interpolator3.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/Interpolator3.java new file mode 100644 index 000000000..1d80fb025 --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/Interpolator3.java @@ -0,0 +1,32 @@ +package com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation; + +/** + * Class for bilinear interpolation of values arranged on a unit square. + */ +public class Interpolator3 { + private final Interpolator bottom; + private final Interpolator top; + + /** + * Constructs an interpolator with given values as vertices of a unit cube. + * * @param _000 The value at (t, u, v) = (0, 0, 0). + * * @param _100 The value at (t, u, v) = (1, 0, 0). + * * @param _010 The value at (t, u, v) = (0, 1, 0). + * * @param _110 The value at (t, u, v) = (1, 1, 0). + * * @param _001 The value at (t, u, v) = (0, 0, 1). + * * @param _101 The value at (t, u, v) = (1, 0, 1). + * * @param _011 The value at (t, u, v) = (0, 1, 1). + * * @param _111 The value at (t, u, v) = (1, 1, 1). + */ + public Interpolator3(double _000, double _100, + double _010, double _110, + double _001, double _101, + double _011, double _111) { + this.top = new Interpolator(_000, _010, _001, _011); + this.bottom = new Interpolator(_100, _110, _101, _111); + } + + public double trilerp(double x, double y, double z) { + return Interpolator.lerp(x, top.bilerp(y, z), bottom.bilerp(y, z)); + } +} \ No newline at end of file diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/samplers/Sampler3D.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/samplers/Sampler3D.java new file mode 100644 index 000000000..96c497f23 --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/samplers/Sampler3D.java @@ -0,0 +1,32 @@ +package com.dfsek.terra.addons.chunkgenerator.generation.math.samplers; + +import net.jafama.FastMath; + +import com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation.ChunkInterpolator3D; +import com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation.ElevationInterpolator; +import com.dfsek.terra.api.world.World; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; +import com.dfsek.terra.api.util.math.Sampler; + + +public class Sampler3D implements Sampler { + private final ChunkInterpolator3D interpolator; + private final ElevationInterpolator elevationInterpolator; + + public Sampler3D(int x, int z, BiomeProvider provider, World world, int elevationSmooth) { + this.interpolator = new ChunkInterpolator3D(world, x, z, provider, (generator, coord) -> generator.getBaseSampler() + .getNoiseSeeded(coord, + world.getSeed())); + this.elevationInterpolator = new ElevationInterpolator(world, x, z, provider, elevationSmooth); + } + + @Override + public double sample(double x, double y, double z) { + return interpolator.getNoise(x, y, z) + elevationInterpolator.getElevation(FastMath.roundToInt(x), FastMath.roundToInt(z)); + } + + @Override + public double sample(int x, int y, int z) { + return interpolator.getNoise(x, y, z) + elevationInterpolator.getElevation(FastMath.roundToInt(x), FastMath.roundToInt(z)); + } +} diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/PaletteHolder.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/PaletteHolder.java new file mode 100644 index 000000000..55d2a2dac --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/PaletteHolder.java @@ -0,0 +1,23 @@ +package com.dfsek.terra.addons.chunkgenerator.palette; + +import com.dfsek.terra.api.world.generator.Palette; + + +public class PaletteHolder { + private final Palette[] palettes; + private final int offset; + + protected PaletteHolder(Palette[] palettes, int offset) { + this.palettes = palettes; + this.offset = offset; + } + + public Palette getPalette(int y) { + int index = y + offset; + return index >= 0 + ? index < palettes.length + ? palettes[index] + : palettes[palettes.length - 1] + : palettes[0]; + } +} diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/PaletteHolderBuilder.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/PaletteHolderBuilder.java new file mode 100644 index 000000000..61042145b --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/PaletteHolderBuilder.java @@ -0,0 +1,38 @@ +package com.dfsek.terra.addons.chunkgenerator.palette; + +import net.jafama.FastMath; + +import java.util.Map; +import java.util.TreeMap; + +import com.dfsek.terra.api.world.generator.Palette; + + +public class PaletteHolderBuilder { + private final TreeMap paletteMap = new TreeMap<>(); + + public PaletteHolderBuilder add(int y, Palette palette) { + paletteMap.put(y, palette); + return this; + } + + public PaletteHolder build() { + + int min = FastMath.min(paletteMap.keySet().stream().min(Integer::compareTo).orElse(0), 0); + int max = FastMath.max(paletteMap.keySet().stream().max(Integer::compareTo).orElse(255), 255); + + Palette[] palettes = new Palette[paletteMap.lastKey() + 1 - min]; + for(int y = min; y <= FastMath.max(paletteMap.lastKey(), max); y++) { + Palette d = null; + for(Map.Entry e : paletteMap.entrySet()) { + if(e.getKey() >= y) { + d = e.getValue(); + break; + } + } + if(d == null) throw new IllegalArgumentException("No palette for Y=" + y); + palettes[y - min] = d; + } + return new PaletteHolder(palettes, -min); + } +} diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/PaletteHolderLoader.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/PaletteHolderLoader.java new file mode 100644 index 000000000..bf0bc3bb5 --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/PaletteHolderLoader.java @@ -0,0 +1,27 @@ +package com.dfsek.terra.addons.chunkgenerator.palette; + +import com.dfsek.tectonic.exception.LoadException; +import com.dfsek.tectonic.loading.ConfigLoader; +import com.dfsek.tectonic.loading.TypeLoader; + +import java.lang.reflect.AnnotatedType; +import java.util.List; +import java.util.Map; + +import com.dfsek.terra.api.world.generator.Palette; + + +public class PaletteHolderLoader implements TypeLoader { + @SuppressWarnings("unchecked") + @Override + public PaletteHolder load(AnnotatedType type, Object o, ConfigLoader configLoader) throws LoadException { + List> palette = (List>) o; + PaletteHolderBuilder builder = new PaletteHolderBuilder(); + for(Map layer : palette) { + for(Map.Entry entry : layer.entrySet()) { + builder.add(entry.getValue(), configLoader.loadType(Palette.class, entry.getKey())); + } + } + return builder.build(); + } +} diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/PaletteInfo.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/PaletteInfo.java new file mode 100644 index 000000000..fd7b331b7 --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/PaletteInfo.java @@ -0,0 +1,36 @@ +package com.dfsek.terra.addons.chunkgenerator.palette; + +import com.dfsek.terra.api.properties.Properties; +import com.dfsek.terra.api.world.generator.Palette; + + +public class PaletteInfo implements Properties { + private final PaletteHolder paletteHolder; + private final SlantHolder slantHolder; + private final Palette ocean; + + private final int seaLevel; + + public PaletteInfo(PaletteHolder paletteHolder, SlantHolder slantHolder, Palette ocean, int seaLevel) { + this.paletteHolder = paletteHolder; + this.slantHolder = slantHolder; + this.ocean = ocean; + this.seaLevel = seaLevel; + } + + public Palette getOcean() { + return ocean; + } + + public PaletteHolder getPaletteHolder() { + return paletteHolder; + } + + public SlantHolder getSlantHolder() { + return slantHolder; + } + + public int getSeaLevel() { + return seaLevel; + } +} diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/SlantHolder.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/SlantHolder.java new file mode 100644 index 000000000..0845604dc --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/SlantHolder.java @@ -0,0 +1,23 @@ +package com.dfsek.terra.addons.chunkgenerator.palette; + + +import java.util.TreeMap; + + +public class SlantHolder { + private final TreeMap layers; + private final double minSlope; + + public SlantHolder(TreeMap layers, double minSlope) { + this.layers = layers; + this.minSlope = minSlope; + } + + public PaletteHolder getPalette(double slope) { + return layers.floorEntry(slope).getValue(); + } + + public double getMinSlope() { + return minSlope; + } +} diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/SlantHolderLoader.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/SlantHolderLoader.java new file mode 100644 index 000000000..74869b9dc --- /dev/null +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/palette/SlantHolderLoader.java @@ -0,0 +1,29 @@ +package com.dfsek.terra.addons.chunkgenerator.palette; + +import com.dfsek.tectonic.exception.LoadException; +import com.dfsek.tectonic.loading.ConfigLoader; +import com.dfsek.tectonic.loading.TypeLoader; + +import java.lang.reflect.AnnotatedType; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + + +@SuppressWarnings("unchecked") +public class SlantHolderLoader implements TypeLoader { + @Override + public SlantHolder load(AnnotatedType type, Object o, ConfigLoader configLoader) throws LoadException { + List> layers = (List>) o; + TreeMap slantLayers = new TreeMap<>(); + double minThreshold = Double.MAX_VALUE; + + for(Map layer : layers) { + double threshold = ((Number) layer.get("threshold")).doubleValue(); + if(threshold < minThreshold) minThreshold = threshold; + slantLayers.put(threshold, configLoader.loadType(PaletteHolder.class, layer.get("palette"))); + } + + return new SlantHolder(slantLayers, minThreshold); + } +}