From 26595773229c86c848a45fbecf14e2529f5b1293 Mon Sep 17 00:00:00 2001 From: dfsek Date: Mon, 5 Oct 2020 20:56:26 -0700 Subject: [PATCH] Implement erosion --- src/main/java/com/dfsek/terra/TerraWorld.java | 29 ++++++++++- .../java/com/dfsek/terra/biome/BiomeZone.java | 25 +++++++++ .../com/dfsek/terra/biome/ErosionNoise.java | 30 +++++++++++ .../com/dfsek/terra/biome/TerraBiomeGrid.java | 18 ++++++- .../dfsek/terra/biome/UserDefinedBiome.java | 8 ++- .../dfsek/terra/config/base/ConfigPack.java | 13 +++++ .../terra/config/genconfig/OreConfig.java | 4 +- .../genconfig/biome/AbstractBiomeConfig.java | 6 +++ .../config/genconfig/biome/BiomeConfig.java | 9 +++- .../generation/{Util.java => DataUtil.java} | 3 +- .../terra/generation/TerraChunkGenerator.java | 10 ++-- .../generation/UserDefinedGenerator.java | 2 +- .../dfsek/terra/population/SnowPopulator.java | 51 ++++++++++++++++++- 13 files changed, 193 insertions(+), 15 deletions(-) create mode 100644 src/main/java/com/dfsek/terra/biome/ErosionNoise.java rename src/main/java/com/dfsek/terra/generation/{Util.java => DataUtil.java} (85%) diff --git a/src/main/java/com/dfsek/terra/TerraWorld.java b/src/main/java/com/dfsek/terra/TerraWorld.java index 7909a7b01..452e77f79 100644 --- a/src/main/java/com/dfsek/terra/TerraWorld.java +++ b/src/main/java/com/dfsek/terra/TerraWorld.java @@ -7,12 +7,14 @@ import com.dfsek.terra.biome.UserDefinedGrid; import com.dfsek.terra.config.base.ConfigPack; import com.dfsek.terra.config.base.ConfigUtil; import com.dfsek.terra.config.base.WorldConfig; +import com.dfsek.terra.config.exception.ConfigException; import com.dfsek.terra.config.genconfig.BiomeGridConfig; import org.bukkit.Bukkit; import org.bukkit.World; import java.util.HashMap; import java.util.Map; +import java.util.Objects; public class TerraWorld { private static final Map map = new HashMap<>(); @@ -20,6 +22,7 @@ public class TerraWorld { private final BiomeZone zone; private final ConfigPack config; private final WorldConfig worldConfig; + private TerraWorld(World w) { worldConfig = new WorldConfig(w, Terra.getInstance()); config = worldConfig.getConfig(); @@ -39,15 +42,37 @@ public class TerraWorld { definedGrids[i] = g.getGrid(w, worldConfig); } } catch(NullPointerException e) { - if(ConfigUtil.debug) e.printStackTrace(); + Debug.stack(e); Bukkit.getLogger().severe("No such BiomeGrid " + partName); Bukkit.getLogger().severe("Please check configuration files for errors. Configuration errors will have been reported during initialization."); Bukkit.getLogger().severe("ONLY report this to Terra if you are SURE your config is error-free."); Bukkit.getLogger().severe("Terrain will NOT generate properly at this point. Correct your config before using your server!"); } } + UserDefinedGrid erosion = null; + if(config.erosionEnable) { + try { + if(config.erosionName.startsWith("BIOME:")) { + UserDefinedBiome[][] temp = new UserDefinedBiome[1][1]; + UserDefinedBiome b = Objects.requireNonNull(config.getBiome(config.erosionName.substring(6)).getBiome()); + temp[0][0] = b; + erosion = new UserDefinedGrid(w, config.freq1, config.freq2, temp, worldConfig); + Debug.info("Loaded single-biome erosion grid " + config.erosionName); + } else { + BiomeGridConfig g = Objects.requireNonNull(config.getBiomeGrid(config.erosionName)); + Debug.info("Loaded BiomeGrid " + g.getID()); + erosion = g.getGrid(w, worldConfig); + } + } catch(NullPointerException e) { + Debug.stack(e); + Bukkit.getLogger().severe("No such BiomeGrid (erosion): " + config.erosionName); + Bukkit.getLogger().severe("Please check configuration files for errors. Configuration errors will have been reported during initialization."); + Bukkit.getLogger().severe("ONLY report this to Terra if you are SURE your config is error-free."); + Bukkit.getLogger().severe("Terrain will NOT generate properly at this point. Correct your config before using your server!"); + } + } zone = new BiomeZone(w, worldConfig, definedGrids); - grid = new TerraBiomeGrid(w, config.freq1, config.freq2, zone, config); + grid = new TerraBiomeGrid(w, config.freq1, config.freq2, zone, config, erosion); } public static synchronized TerraWorld getWorld(World w) { diff --git a/src/main/java/com/dfsek/terra/biome/BiomeZone.java b/src/main/java/com/dfsek/terra/biome/BiomeZone.java index 936a0294f..876dad8c3 100644 --- a/src/main/java/com/dfsek/terra/biome/BiomeZone.java +++ b/src/main/java/com/dfsek/terra/biome/BiomeZone.java @@ -10,6 +10,9 @@ import org.polydev.gaea.math.FastNoise; import java.util.Objects; +/** + * Holds 1D array of BiomeGrids. + */ public class BiomeZone { private final BiomeGrid[] grids; private final FastNoise noise; @@ -29,18 +32,40 @@ public class BiomeZone { channel = wc.zoneChannel; } + /** + * Get BiomeGrid at location + * @param x X coordinate + * @param z Z coordinate + * @return BiomeGrid at coordinates. + */ protected BiomeGrid getGrid(int x, int z) { return grids[NormalizationUtil.normalize(useImage ? Objects.requireNonNull(imageLoader).getNoiseVal(x, z, channel) : noise.getNoise(x, z), grids.length)]; } + /** + * Get the number of BiomeGrids this BiomeZone holds. + * @return Number of grids + */ public int getSize() { return grids.length; } + /** + * Get the normalized grid noise at location + * @param x X coordinate + * @param z Z coordinate + * @return Normalized noise at coordinates + */ public int getNoise(int x, int z) { return NormalizationUtil.normalize(useImage ? Objects.requireNonNull(imageLoader).getNoiseVal(x, z, channel) : noise.getNoise(x, z), grids.length); } + /** + * Get raw grid noise at location + * @param x X coordinate + * @param z Z coordinate + * @return Raw noise at coordinates + */ public double getRawNoise(int x, int z) { return useImage ? Objects.requireNonNull(imageLoader).getNoiseVal(x, z, channel) : noise.getNoise(x, z); } diff --git a/src/main/java/com/dfsek/terra/biome/ErosionNoise.java b/src/main/java/com/dfsek/terra/biome/ErosionNoise.java new file mode 100644 index 000000000..a9e2c08d7 --- /dev/null +++ b/src/main/java/com/dfsek/terra/biome/ErosionNoise.java @@ -0,0 +1,30 @@ +package com.dfsek.terra.biome; + +import org.polydev.gaea.math.FastNoise; + +/** + * Class to hold noise function to determine erosion. + */ +public class ErosionNoise { + private final double thresh; + private final FastNoise noise; + public ErosionNoise(float freq1, double thresh, long seed) { + FastNoise main = new FastNoise((int) (seed+1)); + main.setNoiseType(FastNoise.NoiseType.SimplexFractal); + main.setFractalOctaves(2); + main.setFrequency(freq1); + this.thresh = thresh; + this.noise = main; + } + + /** + * Get whether a location is eroded + * @param x X coordinate + * @param z Z coordinate + * @return Whether location is eroded + */ + boolean isEroded(int x, int z) { + double abs = Math.pow(noise.getNoise(x, z), 2); + return abs < thresh; + } +} diff --git a/src/main/java/com/dfsek/terra/biome/TerraBiomeGrid.java b/src/main/java/com/dfsek/terra/biome/TerraBiomeGrid.java index ca1b91e54..b77ac0c34 100644 --- a/src/main/java/com/dfsek/terra/biome/TerraBiomeGrid.java +++ b/src/main/java/com/dfsek/terra/biome/TerraBiomeGrid.java @@ -9,20 +9,29 @@ import org.polydev.gaea.biome.Biome; import org.polydev.gaea.biome.BiomeGrid; import org.polydev.gaea.generation.GenerationPhase; +import java.util.HashMap; +import java.util.Map; + public class TerraBiomeGrid extends BiomeGrid { private static int failNum = 0; private CoordinatePerturb perturb; + private ErosionNoise erode; + private UserDefinedGrid erosionGrid; private final BiomeZone zone; private final boolean perturbPaletteOnly; - public TerraBiomeGrid(World w, float freq1, float freq2, BiomeZone zone, ConfigPack c) { + public TerraBiomeGrid(World w, float freq1, float freq2, BiomeZone zone, ConfigPack c, UserDefinedGrid erosion) { super(w, freq1, freq2); if(c.biomeBlend) { perturb = new CoordinatePerturb(c.blendFreq, c.blendAmp, w.getSeed()); } perturbPaletteOnly = c.perturbPaletteOnly; this.zone = zone; + if(c.erosionEnable) { + erode = new ErosionNoise(c.erosionFreq, c.erosionThresh, w.getSeed()); + this.erosionGrid = erosion; + } } @Override @@ -35,14 +44,19 @@ public class TerraBiomeGrid extends BiomeGrid { zp = perturbCoords[1]; } + UserDefinedBiome b; try { - return zone.getGrid(xp, zp).getBiome(xp, zp, phase); + b = (UserDefinedBiome) zone.getGrid(xp, zp).getBiome(xp, zp, phase); } catch(NullPointerException e) { if(ConfigUtil.debug) e.printStackTrace(); if(failNum % 256 == 0) Bukkit.getLogger().severe("[Terra] A severe configuration error has prevented Terra from properly generating terrain at coordinates: " + x + ", " + z + ". Please check your configuration for errors. Any config errors will have been reported above."); failNum++; return null; } + if(erode != null && b.isErodible() && erode.isEroded(xp, zp)) { + return erosionGrid.getBiome(xp, zp, phase); + } + return b; } @Override diff --git a/src/main/java/com/dfsek/terra/biome/UserDefinedBiome.java b/src/main/java/com/dfsek/terra/biome/UserDefinedBiome.java index 1555be411..a71208366 100644 --- a/src/main/java/com/dfsek/terra/biome/UserDefinedBiome.java +++ b/src/main/java/com/dfsek/terra/biome/UserDefinedBiome.java @@ -14,13 +14,15 @@ public class UserDefinedBiome implements Biome { private final UserDefinedDecorator decorator; private final org.bukkit.block.Biome vanilla; private final String id; + private final boolean erode; - public UserDefinedBiome(org.bukkit.block.Biome vanilla, UserDefinedDecorator dec, UserDefinedGenerator gen, String id) { + public UserDefinedBiome(org.bukkit.block.Biome vanilla, UserDefinedDecorator dec, UserDefinedGenerator gen, boolean erode, String id) { this.vanilla = vanilla; this.decorator = dec; this.gen = gen; this.id = id; + this.erode = erode; } /** @@ -66,4 +68,8 @@ public class UserDefinedBiome implements Biome { public String getID() { return id; } + + public boolean isErodible() { + return erode; + } } diff --git a/src/main/java/com/dfsek/terra/config/base/ConfigPack.java b/src/main/java/com/dfsek/terra/config/base/ConfigPack.java index 3b6aca0ab..7c7daf84a 100644 --- a/src/main/java/com/dfsek/terra/config/base/ConfigPack.java +++ b/src/main/java/com/dfsek/terra/config/base/ConfigPack.java @@ -1,6 +1,8 @@ package com.dfsek.terra.config.base; +import com.dfsek.terra.Debug; import com.dfsek.terra.biome.UserDefinedBiome; +import com.dfsek.terra.biome.UserDefinedGrid; import com.dfsek.terra.carving.UserDefinedCarver; import com.dfsek.terra.config.ConfigLoader; import com.dfsek.terra.config.exception.ConfigException; @@ -50,6 +52,11 @@ public class ConfigPack extends YamlConfiguration { public float freq1; public float freq2; + public float erosionFreq; + public double erosionThresh; + public boolean erosionEnable; + public String erosionName; + public int blendAmp; public boolean biomeBlend; public float blendFreq; @@ -89,6 +96,12 @@ public class ConfigPack extends YamlConfiguration { blendFreq = (float) getDouble("blend.frequency", 0.01); perturbPaletteOnly = getBoolean("blend.ignore-terrain", true); + erosionEnable = getBoolean("erode.enable", false); + erosionFreq = (float) getDouble("erode.frequency", 0.01); + erosionThresh = getDouble("erode.threshold", 0.04); + + erosionName = getString("erode.grid"); + // Load BiomeGrids from BiomeZone biomeList = getStringList("grids"); diff --git a/src/main/java/com/dfsek/terra/config/genconfig/OreConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/OreConfig.java index 7e4c1140e..481278cea 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/OreConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/OreConfig.java @@ -36,8 +36,8 @@ public class OreConfig extends TerraConfig { if(!contains("replace")) throw new ConfigException("Ore replaceable materials not found!", getID()); min = getInt("radius.min", 1); max = getInt("radius.max", 1); - deform = getDouble("deform"); - deformFrequency = getDouble("deform-frequency"); + deform = getDouble("deform", 0.75); + deformFrequency = getDouble("deform-frequency", 0.1); update = getBoolean("update", false); replaceable = ConfigUtil.toBlockData(getStringList("replace"), "replaceable", getID()); diff --git a/src/main/java/com/dfsek/terra/config/genconfig/biome/AbstractBiomeConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/biome/AbstractBiomeConfig.java index c8cc87f4b..f68feb56d 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/biome/AbstractBiomeConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/biome/AbstractBiomeConfig.java @@ -17,6 +17,7 @@ public class AbstractBiomeConfig extends TerraConfig { private final int treeDensity; private final String equation; private final int floraAttempts; + private final int snowChance; private double slabThreshold; private BiomeSlabConfig slabs; private boolean useStairs; @@ -51,6 +52,7 @@ public class AbstractBiomeConfig extends TerraConfig { floraFreq = (float) getDouble("flora-simplex.frequency", 0.1); floraSeed = getInt("flora-simplex.seed", 0); seaLevel = getInt("ocean.level", 62); + snowChance = getInt("snow-chance", 0); oceanPalette = getString("ocean.palette"); useStairs = getBoolean("slabs.use-stairs-if-available", false); @@ -145,4 +147,8 @@ public class AbstractBiomeConfig extends TerraConfig { public List getStructureConfigs() { return structureConfigs; } + + public int getSnowChance() { + return snowChance; + } } 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 223b8cbec..e40ee858c 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 @@ -49,6 +49,7 @@ public class BiomeConfig extends TerraConfig { private FastNoise floraNoise; private final Palette ocean; private int seaLevel; + private int snowChance; private final List structures; private final ConfigPack config; @@ -87,6 +88,7 @@ public class BiomeConfig extends TerraConfig { floraSimplex = getBoolean("flora-simplex.enable", Objects.requireNonNull(abstractBiome).isFloraSimplex()); floraFreq = (float) getDouble("flora-simplex.frequency", Objects.requireNonNull(abstractBiome).getFloraFreq()); seaLevel = getInt("ocean.level", Objects.requireNonNull(abstractBiome).getSeaLevel()); + snowChance = getInt("snow-chance", Objects.requireNonNull(abstractBiome).getSnowChance()); eq = getString("noise-equation", Objects.requireNonNull(abstractBiome).getEquation()); } catch(NullPointerException e) { slabThreshold = getDouble("slabs.threshold", 0.1D); @@ -98,6 +100,7 @@ public class BiomeConfig extends TerraConfig { floraSimplex = getBoolean("flora-simplex.enable", false); floraFreq = (float) getDouble("flora-simplex.frequency", 0.1); seaLevel = getInt("ocean.level", 62); + snowChance = getInt("snow-chance", 0); eq = getString("noise-equation", null); } @@ -203,7 +206,7 @@ public class BiomeConfig extends TerraConfig { try { // Get UserDefinedBiome instance representing this config. - this.biome = new UserDefinedBiome(vanillaBiome, dec, new UserDefinedGenerator(eq, Collections.emptyList(), palette.getPaletteMap()), biomeID); + this.biome = new UserDefinedBiome(vanillaBiome, dec, new UserDefinedGenerator(eq, Collections.emptyList(), palette.getPaletteMap()), getBoolean("erodible", false), biomeID); } catch(ParseException e) { e.printStackTrace(); throw new ConfigException("Unable to parse noise equation!", getID()); @@ -278,4 +281,8 @@ public class BiomeConfig extends TerraConfig { public Range getTreeRange(Tree t) { return tree.getTreeHeights().getOrDefault(t, new Range(-1, -1)); } + + public int getSnowChance() { + return snowChance; + } } diff --git a/src/main/java/com/dfsek/terra/generation/Util.java b/src/main/java/com/dfsek/terra/generation/DataUtil.java similarity index 85% rename from src/main/java/com/dfsek/terra/generation/Util.java rename to src/main/java/com/dfsek/terra/generation/DataUtil.java index 0942b5fdb..bbe2103e6 100644 --- a/src/main/java/com/dfsek/terra/generation/Util.java +++ b/src/main/java/com/dfsek/terra/generation/DataUtil.java @@ -7,8 +7,9 @@ import org.polydev.gaea.world.palette.RandomPalette; import java.util.Random; -public class Util { +public class DataUtil { public static final BlockData STONE = Material.STONE.createBlockData(); + public static final BlockData SNOW = Material.SNOW.createBlockData(); public static final BlockData WATER = Material.WATER.createBlockData(); public static final BlockData AIR = Material.AIR.createBlockData(); public static final Palette BLANK_PALETTE = new RandomPalette(new Random(2403)).add(AIR, 1); diff --git a/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java b/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java index a7a0241bc..6cfcfc865 100644 --- a/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java +++ b/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java @@ -9,6 +9,7 @@ import com.dfsek.terra.config.genconfig.biome.BiomeConfig; import com.dfsek.terra.population.CavePopulator; import com.dfsek.terra.population.FloraPopulator; import com.dfsek.terra.population.OrePopulator; +import com.dfsek.terra.population.SnowPopulator; import com.dfsek.terra.population.StructurePopulator; import com.dfsek.terra.population.TreePopulator; import com.dfsek.terra.structure.StructureSpawnRequirement; @@ -52,6 +53,7 @@ public class TerraChunkGenerator extends GaeaChunkGenerator { popMan.attach(new TreePopulator()); popMan.attach(new FloraPopulator()); popMan.attach(new OrePopulator()); + popMan.attach(new SnowPopulator()); } @Override @@ -102,16 +104,16 @@ public class TerraChunkGenerator extends GaeaChunkGenerator { BlockData stair = stairPalette.get(0, block.getBlockX(), block.getBlockZ()); Stairs finalStair = getStair(new double[] {_01, _10, _12, _21}, (Stairs) stair, thresh); if(finalStair != null) { - if(orig.matches(Util.WATER)) finalStair.setWaterlogged(true); + if(orig.matches(DataUtil.WATER)) finalStair.setWaterlogged(true); chunk.setBlock(block.getBlockX(), block.getBlockY(), block.getBlockZ(), finalStair); return; } } } - BlockData slab = slabs.getOrDefault(down.getMaterial(), Util.BLANK_PALETTE).get(0, block.getBlockX(), block.getBlockZ()); + BlockData slab = slabs.getOrDefault(down.getMaterial(), DataUtil.BLANK_PALETTE).get(0, block.getBlockX(), block.getBlockZ()); if(slab instanceof Waterlogged) { - ((Waterlogged) slab).setWaterlogged(orig.matches(Util.WATER)); - } else if(orig.matches(Util.WATER)) return; + ((Waterlogged) slab).setWaterlogged(orig.matches(DataUtil.WATER)); + } else if(orig.matches(DataUtil.WATER)) return; chunk.setBlock(block.getBlockX(), block.getBlockY(), block.getBlockZ(), slab); } } diff --git a/src/main/java/com/dfsek/terra/generation/UserDefinedGenerator.java b/src/main/java/com/dfsek/terra/generation/UserDefinedGenerator.java index 991e450d3..9ff6770db 100644 --- a/src/main/java/com/dfsek/terra/generation/UserDefinedGenerator.java +++ b/src/main/java/com/dfsek/terra/generation/UserDefinedGenerator.java @@ -36,7 +36,7 @@ public class UserDefinedGenerator extends Generator { p.registerFunction("noise2", n2); p.registerFunction("noise3", n3); for(int y = 0; y < 256; y++) { - Palette d = Util.BLANK_PALETTE; + Palette d = DataUtil.BLANK_PALETTE; for(Map.Entry> e : pa.entrySet()) { if(e.getKey() >= y) { d = e.getValue(); diff --git a/src/main/java/com/dfsek/terra/population/SnowPopulator.java b/src/main/java/com/dfsek/terra/population/SnowPopulator.java index a6ea5e38c..257304dec 100644 --- a/src/main/java/com/dfsek/terra/population/SnowPopulator.java +++ b/src/main/java/com/dfsek/terra/population/SnowPopulator.java @@ -1,15 +1,64 @@ package com.dfsek.terra.population; +import com.dfsek.terra.TerraWorld; +import com.dfsek.terra.biome.TerraBiomeGrid; +import com.dfsek.terra.biome.UserDefinedBiome; +import com.dfsek.terra.config.base.ConfigUtil; +import com.dfsek.terra.generation.DataUtil; +import org.bukkit.Bukkit; import org.bukkit.Chunk; +import org.bukkit.Material; import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; import org.jetbrains.annotations.NotNull; +import org.polydev.gaea.generation.GenerationPhase; import org.polydev.gaea.population.GaeaBlockPopulator; +import java.util.ArrayList; +import java.util.HashSet; import java.util.Random; +import java.util.Set; public class SnowPopulator extends GaeaBlockPopulator { + private static final Set blacklistSpawn = new HashSet<>(); + private static final BlockData snow = Material.SNOW.createBlockData(); + static { + for(Material m : Material.values()) { + String name = m.toString().toLowerCase(); + if(name.contains("slab") + || name.contains("stair") + || name.contains("wall") + || name.contains("fence") + || name.contains("lantern") + || name.contains("chest") + || name.contains("door") + || name.contains("repeater") + || name.equals("lily_pad") + || name.equals("snow")) blacklistSpawn.add(m); + } + blacklistSpawn.add(Material.END_STONE); + if(ConfigUtil.debug) + Bukkit.getLogger().info("Added " + blacklistSpawn.size() + " materials to snow blacklist"); + } @Override public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk chunk) { - + int origX = chunk.getX() << 4; + int origZ = chunk.getZ() << 4; + TerraWorld w = TerraWorld.getWorld(world); + TerraBiomeGrid g = w.getGrid(); + for(int x = 0; x < 16; x++) { + for(int z = 0; z < 16; z++) { + if(random.nextInt(100) >= w.getConfig().getBiome((UserDefinedBiome) g.getBiome(origX+x, origZ+z, GenerationPhase.PALETTE_APPLY)).getSnowChance()) continue; + int y; + Block b = null; + for(y = 254; y > 0; y--) { + b = chunk.getBlock(x, y, z); + if(!b.getType().isAir()) break; + } + if(blacklistSpawn.contains(b.getType()) || b.isPassable()) continue; + chunk.getBlock(x, ++y, z).setBlockData(DataUtil.SNOW); + } + } } }