Add advanced biome blending

This commit is contained in:
dfsek 2020-09-27 16:11:36 -07:00
parent 81d607b900
commit cfbd6533dd
15 changed files with 87 additions and 28 deletions

View File

@ -95,7 +95,7 @@
<dependency> <dependency>
<groupId>org.polydev</groupId> <groupId>org.polydev</groupId>
<artifactId>gaea</artifactId> <artifactId>gaea</artifactId>
<version>1.10.45</version> <version>1.10.46</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>me.lucko</groupId> <groupId>me.lucko</groupId>

View File

@ -26,6 +26,7 @@ import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.polydev.gaea.generation.GenerationPhase;
import org.polydev.gaea.profiler.WorldProfiler; import org.polydev.gaea.profiler.WorldProfiler;
import java.io.File; import java.io.File;
@ -46,7 +47,7 @@ public class TerraCommand implements CommandExecutor, TabExecutor {
return true; return true;
case "biome": case "biome":
if(! (sender instanceof Player)) return false; if(! (sender instanceof Player)) return false;
sender.sendMessage("You are in " + BiomeConfig.fromBiome((UserDefinedBiome) TerraBiomeGrid.fromWorld(((Player) sender).getWorld()).getBiome(((Player) sender).getLocation())).getFriendlyName()); sender.sendMessage("You are in " + BiomeConfig.fromBiome((UserDefinedBiome) TerraBiomeGrid.fromWorld(((Player) sender).getWorld()).getBiome(((Player) sender).getLocation(), GenerationPhase.POPULATE)).getFriendlyName());
return true; return true;
case "profile": case "profile":
if(! (sender instanceof Player)) { if(! (sender instanceof Player)) {

View File

@ -0,0 +1,21 @@
package com.dfsek.terra.biome;
import org.polydev.gaea.math.FastNoise;
public class CoordinatePerturb {
private final FastNoise perturbX;
private final FastNoise perturbZ;
private final int amplitude;
public CoordinatePerturb(float frequency, int amplitude, long seed) {
perturbX = new FastNoise((int) seed);
perturbX.setNoiseType(FastNoise.NoiseType.Simplex);
perturbX.setFrequency(frequency);
perturbZ = new FastNoise((int) seed+1);
perturbZ.setNoiseType(FastNoise.NoiseType.Simplex);
perturbZ.setFrequency(frequency);
this.amplitude = amplitude;
}
public int[] getShiftedCoords(int x, int z) {
return new int[] {(int) (perturbX.getNoise(x, z)*amplitude+x), (int) (perturbZ.getNoise(x, z)*amplitude+z)};
}
}

View File

@ -7,21 +7,29 @@ import org.bukkit.Location;
import org.bukkit.World; import org.bukkit.World;
import org.polydev.gaea.biome.Biome; import org.polydev.gaea.biome.Biome;
import org.polydev.gaea.biome.BiomeGrid; import org.polydev.gaea.biome.BiomeGrid;
import org.polydev.gaea.generation.GenerationPhase;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
public class TerraBiomeGrid extends BiomeGrid { public class TerraBiomeGrid extends BiomeGrid {
private static int failNum = 0; private static int failNum = 0;
private CoordinatePerturb perturb;
private static final Map<World, TerraBiomeGrid> grids = new HashMap<>(); private static final Map<World, TerraBiomeGrid> grids = new HashMap<>();
private final World w; private final World w;
private final BiomeZone zone; private final BiomeZone zone;
private final boolean perturbPaletteOnly;
private TerraBiomeGrid(World w, float freq1, float freq2, boolean blank) { private TerraBiomeGrid(World w, float freq1, float freq2, boolean blank) {
super(w, freq1, freq2); super(w, freq1, freq2);
WorldConfig c = WorldConfig.fromWorld(w);
if(c.biomeBlend) {
perturb = new CoordinatePerturb(c.blendFreq, c.blendAmp, w.getSeed());
}
perturbPaletteOnly = c.perturbPaletteOnly;
this.w = w; this.w = w;
this.zone = BiomeZone.fromWorld(w); this.zone = BiomeZone.fromWorld(w);
if(!blank) grids.put(w, this); if(!blank) grids.put(w, this);
@ -41,9 +49,17 @@ public class TerraBiomeGrid extends BiomeGrid {
} }
@Override @Override
public Biome getBiome(int x, int z) { public Biome getBiome(int x, int z, GenerationPhase phase) {
int xp = x;
int zp = z;
if(perturb != null && (!perturbPaletteOnly || phase.equals(GenerationPhase.PALETTE_APPLY))) {
int[] perturbCoords = perturb.getShiftedCoords(x, z);
xp = perturbCoords[0];
zp = perturbCoords[1];
}
try { try {
return zone.getGrid(x, z).getBiome(x, z); return zone.getGrid(xp, zp).getBiome(xp, zp, phase);
} catch(NullPointerException e) { } catch(NullPointerException e) {
if(ConfigUtil.debug) e.printStackTrace(); 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."); 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.");
@ -53,8 +69,8 @@ public class TerraBiomeGrid extends BiomeGrid {
} }
@Override @Override
public Biome getBiome(Location l) { public Biome getBiome(Location l, GenerationPhase phase) {
return getBiome(l.getBlockX(), l.getBlockZ()); return getBiome(l.getBlockX(), l.getBlockZ(), phase);
} }
public static void invalidate() { public static void invalidate() {

View File

@ -8,6 +8,8 @@ import org.bukkit.World;
import org.polydev.gaea.biome.Biome; import org.polydev.gaea.biome.Biome;
import org.polydev.gaea.biome.BiomeGrid; import org.polydev.gaea.biome.BiomeGrid;
import org.polydev.gaea.biome.NormalizationUtil; import org.polydev.gaea.biome.NormalizationUtil;
import org.polydev.gaea.generation.GenerationPhase;
import org.polydev.gaea.math.Interpolator;
public class UserDefinedGrid extends BiomeGrid { public class UserDefinedGrid extends BiomeGrid {
private final ImageLoader imageLoader; private final ImageLoader imageLoader;
@ -26,17 +28,17 @@ public class UserDefinedGrid extends BiomeGrid {
} }
@Override @Override
public Biome getBiome(int x, int z) { public Biome getBiome(int x, int z, GenerationPhase phase) {
if(fromImage) { if(fromImage) {
double xi = imageLoader.getNoiseVal(x, z, channelX); double xi = imageLoader.getNoiseVal(x, z, channelX);
double zi = imageLoader.getNoiseVal(x, z, channelZ); double zi = imageLoader.getNoiseVal(x, z, channelZ);
return super.getGrid()[NormalizationUtil.normalize(xi, getSizeX())][NormalizationUtil.normalize(zi, getSizeZ())]; return super.getGrid()[NormalizationUtil.normalize(xi, getSizeX())][NormalizationUtil.normalize(zi, getSizeZ())];
} }
return super.getBiome(x, z); return super.getBiome(x, z, phase);
} }
@Override @Override
public Biome getBiome(Location l) { public Biome getBiome(Location l, GenerationPhase phase) {
return this.getBiome(l.getBlockX(), l.getBlockZ()); return this.getBiome(l.getBlockX(), l.getBlockZ(), phase);
} }
} }

View File

@ -6,6 +6,7 @@ import com.dfsek.terra.biome.UserDefinedBiome;
import com.dfsek.terra.config.genconfig.BiomeConfig; import com.dfsek.terra.config.genconfig.BiomeConfig;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.polydev.gaea.generation.GenerationPhase;
import org.polydev.gaea.world.carving.Carver; import org.polydev.gaea.world.carving.Carver;
import org.polydev.gaea.world.carving.Worm; import org.polydev.gaea.world.carving.Worm;
@ -40,7 +41,7 @@ public class UserDefinedCarver extends Carver {
@Override @Override
public boolean isChunkCarved(World w, int chunkX, int chunkZ, Random random) { public boolean isChunkCarved(World w, int chunkX, int chunkZ, Random random) {
return random.nextInt(100) < BiomeConfig.fromBiome((UserDefinedBiome) TerraBiomeGrid.fromWorld(w).getBiome(chunkX << 4, chunkZ << 4)).getCarverChance(this); return random.nextInt(100) < BiomeConfig.fromBiome((UserDefinedBiome) TerraBiomeGrid.fromWorld(w).getBiome(chunkX << 4, chunkZ << 4, GenerationPhase.POPULATE)).getCarverChance(this);
} }
private class UserDefinedWorm extends Worm { private class UserDefinedWorm extends Worm {

View File

@ -34,8 +34,11 @@ public class WorldConfig {
public ImageLoader.Channel biomeXChannel; public ImageLoader.Channel biomeXChannel;
public ImageLoader.Channel biomeZChannel; public ImageLoader.Channel biomeZChannel;
public ImageLoader.Channel zoneChannel; public ImageLoader.Channel zoneChannel;
public ImageLoader.Channel terrainChannel; public boolean biomeBlend;
public ImageLoader imageLoader; public ImageLoader imageLoader;
public int blendAmp;
public float blendFreq;
public boolean perturbPaletteOnly;
public WorldConfig(World w, JavaPlugin main) { public WorldConfig(World w, JavaPlugin main) {
@ -74,6 +77,12 @@ public class WorldConfig {
freq1 = 1f/config.getInt("frequencies.grid-x", 256); freq1 = 1f/config.getInt("frequencies.grid-x", 256);
freq2 = 1f/config.getInt("frequencies.grid-z", 512); freq2 = 1f/config.getInt("frequencies.grid-z", 512);
fromImage = config.getBoolean("image.use-image", false); fromImage = config.getBoolean("image.use-image", false);
biomeBlend = config.getBoolean("blend.enable", false);
blendAmp = config.getInt("blend.amplitude", 8);
blendFreq = (float) config.getDouble("blend.frequency", 0.01);
perturbPaletteOnly = config.getBoolean("blend.ignore-terrain", true);
// Load image stuff // Load image stuff
try { try {

View File

@ -13,6 +13,7 @@ import org.bukkit.block.data.type.Slab;
import org.bukkit.block.data.type.Stairs; import org.bukkit.block.data.type.Stairs;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.polydev.gaea.generation.GenerationPhase;
import org.polydev.gaea.generation.GenerationPopulator; import org.polydev.gaea.generation.GenerationPopulator;
import org.polydev.gaea.math.ChunkInterpolator; import org.polydev.gaea.math.ChunkInterpolator;
import org.polydev.gaea.world.palette.Palette; import org.polydev.gaea.world.palette.Palette;
@ -32,7 +33,7 @@ public class SlabGenerator extends GenerationPopulator {
for(byte z = 0; z < 16; z++) { for(byte z = 0; z < 16; z++) {
int xi = (chunkX << 4) + x; int xi = (chunkX << 4) + x;
int zi = (chunkZ << 4) + z; int zi = (chunkZ << 4) + z;
BiomeConfig config = BiomeConfig.fromBiome((UserDefinedBiome) g.getBiome(xi, zi)); BiomeConfig config = BiomeConfig.fromBiome((UserDefinedBiome) g.getBiome(xi, zi, GenerationPhase.PALETTE_APPLY));
if(config.getSlabs() == null) continue; if(config.getSlabs() == null) continue;
double thresh = config.getSlabThreshold(); double thresh = config.getSlabThreshold();
for(int y = 0; y < world.getMaxHeight(); y++) { for(int y = 0; y < world.getMaxHeight(); y++) {

View File

@ -1,10 +1,14 @@
package com.dfsek.terra.generation; package com.dfsek.terra.generation;
import com.dfsek.terra.biome.TerraBiomeGrid;
import com.dfsek.terra.biome.UserDefinedBiome;
import com.dfsek.terra.biome.UserDefinedGrid;
import com.dfsek.terra.math.NoiseFunction2; import com.dfsek.terra.math.NoiseFunction2;
import com.dfsek.terra.math.NoiseFunction3; import com.dfsek.terra.math.NoiseFunction3;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.polydev.gaea.biome.Generator; import org.polydev.gaea.biome.Generator;
import org.polydev.gaea.generation.GenerationPhase;
import org.polydev.gaea.math.FastNoise; import org.polydev.gaea.math.FastNoise;
import org.polydev.gaea.math.parsii.eval.Expression; import org.polydev.gaea.math.parsii.eval.Expression;
import org.polydev.gaea.math.parsii.eval.Parser; import org.polydev.gaea.math.parsii.eval.Parser;
@ -20,9 +24,9 @@ import java.util.TreeMap;
public class UserDefinedGenerator extends Generator { public class UserDefinedGenerator extends Generator {
private final Expression noiseExp; private final Expression noiseExp;
private final Scope s = new Scope(); private final Scope s = new Scope();
private final Variable xVar = s.getVariable("x");; private final Variable xVar = s.getVariable("x");
private final Variable yVar = s.getVariable("y"); private final Variable yVar = s.getVariable("y");
private final Variable zVar = s.getVariable("z");; private final Variable zVar = s.getVariable("z");
private final TreeMap<Integer, Palette<BlockData>> paletteMap; private final TreeMap<Integer, Palette<BlockData>> paletteMap;
private final NoiseFunction2 n2 = new NoiseFunction2(); private final NoiseFunction2 n2 = new NoiseFunction2();
private final NoiseFunction3 n3 = new NoiseFunction3(); private final NoiseFunction3 n3 = new NoiseFunction3();
@ -51,8 +55,8 @@ public class UserDefinedGenerator extends Generator {
xVar.setValue(x); xVar.setValue(x);
yVar.setValue(0); yVar.setValue(0);
zVar.setValue(z); zVar.setValue(z);
n2.setNoise(gen, false); n2.setNoise(gen);
n3.setNoise(gen, false); n3.setNoise(gen);
return noiseExp.evaluate(); return noiseExp.evaluate();
} }
} }
@ -72,8 +76,8 @@ public class UserDefinedGenerator extends Generator {
xVar.setValue(x); xVar.setValue(x);
yVar.setValue(y); yVar.setValue(y);
zVar.setValue(z); zVar.setValue(z);
n2.setNoise(gen, false); n2.setNoise(gen);
n3.setNoise(gen, false); n3.setNoise(gen);
return noiseExp.evaluate(); return noiseExp.evaluate();
} }
} }

View File

@ -7,6 +7,7 @@ import com.dfsek.terra.config.WorldConfig;
import com.dfsek.terra.config.genconfig.BiomeConfig; import com.dfsek.terra.config.genconfig.BiomeConfig;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.polydev.gaea.generation.GenerationPhase;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
@ -37,7 +38,7 @@ public class DebugFrame extends JFrame implements ActionListener {
xp = (int) (((double) Math.floorMod(p.getLocation().getBlockX() - (img.getWidth() / 2), x) / x) * getWidth()); xp = (int) (((double) Math.floorMod(p.getLocation().getBlockX() - (img.getWidth() / 2), x) / x) * getWidth());
zp = (int) (((double) Math.floorMod(p.getLocation().getBlockZ() - (img.getHeight() / 2), z) / z) * getHeight()); zp = (int) (((double) Math.floorMod(p.getLocation().getBlockZ() - (img.getHeight() / 2), z) / z) * getHeight());
} }
String str = BiomeConfig.fromBiome((UserDefinedBiome) TerraBiomeGrid.fromWorld(p.getWorld()).getBiome(p.getLocation())).getID(); String str = BiomeConfig.fromBiome((UserDefinedBiome) TerraBiomeGrid.fromWorld(p.getWorld()).getBiome(p.getLocation(), GenerationPhase.POPULATE)).getID();
g.setColor(new Color(255, 255, 255, 128)); g.setColor(new Color(255, 255, 255, 128));
g.fillRect(xp + 13, zp - 13, (int) (8 + 8.25 * str.length()), 33); g.fillRect(xp + 13, zp - 13, (int) (8 + 8.25 * str.length()), 33);
g.setColor(Color.BLACK); g.setColor(Color.BLACK);

View File

@ -19,8 +19,8 @@ public class NoiseFunction2 implements Function {
return gen.getSimplexFractal((float) list.get(0).evaluate(), (float) list.get(1).evaluate()); return gen.getSimplexFractal((float) list.get(0).evaluate(), (float) list.get(1).evaluate());
} }
public void setNoise(FastNoise gen, boolean override) { public void setNoise(FastNoise gen) {
if(this.gen == null || override) this.gen = gen; this.gen = gen;
} }
@Override @Override

View File

@ -18,8 +18,8 @@ public class NoiseFunction3 implements Function {
return gen.getSimplexFractal((float) list.get(0).evaluate(), (float) list.get(1).evaluate(), (float) list.get(2).evaluate()); return gen.getSimplexFractal((float) list.get(0).evaluate(), (float) list.get(1).evaluate(), (float) list.get(2).evaluate());
} }
public void setNoise(FastNoise gen, boolean override) { public void setNoise(FastNoise gen) {
if(this.gen == null || override) this.gen = gen; this.gen = gen;
} }
@Override @Override

View File

@ -10,6 +10,7 @@ import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.polydev.gaea.biome.Biome; import org.polydev.gaea.biome.Biome;
import org.polydev.gaea.generation.GenerationPhase;
import org.polydev.gaea.population.GaeaBlockPopulator; import org.polydev.gaea.population.GaeaBlockPopulator;
import org.polydev.gaea.profiler.ProfileFuture; import org.polydev.gaea.profiler.ProfileFuture;
import org.polydev.gaea.world.Flora; import org.polydev.gaea.world.Flora;
@ -27,7 +28,7 @@ public class FloraPopulator extends GaeaBlockPopulator {
pop.add(chunk); pop.add(chunk);
for(int x = 0; x < 16; x++) { for(int x = 0; x < 16; x++) {
for(int z = 0; z < 16; z++) { for(int z = 0; z < 16; z++) {
UserDefinedBiome biome = (UserDefinedBiome) TerraBiomeGrid.fromWorld(world).getBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z); UserDefinedBiome biome = (UserDefinedBiome) TerraBiomeGrid.fromWorld(world).getBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z, GenerationPhase.POPULATE);
if(biome.getDecorator().getFloraChance() <= 0 || random.nextInt(100) > biome.getDecorator().getFloraChance()) if(biome.getDecorator().getFloraChance() <= 0 || random.nextInt(100) > biome.getDecorator().getFloraChance())
continue; continue;
try { try {

View File

@ -11,6 +11,7 @@ import org.bukkit.Location;
import org.bukkit.World; import org.bukkit.World;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.polydev.gaea.biome.Biome; import org.polydev.gaea.biome.Biome;
import org.polydev.gaea.generation.GenerationPhase;
import org.polydev.gaea.population.GaeaBlockPopulator; import org.polydev.gaea.population.GaeaBlockPopulator;
import org.polydev.gaea.profiler.ProfileFuture; import org.polydev.gaea.profiler.ProfileFuture;
@ -22,7 +23,7 @@ public class OrePopulator extends GaeaBlockPopulator {
public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk chunk) { public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk chunk) {
try (ProfileFuture ignored = TerraProfiler.fromWorld(world).measure("OreTime")) { try (ProfileFuture ignored = TerraProfiler.fromWorld(world).measure("OreTime")) {
Location l = chunk.getBlock(8, 0, 0).getLocation(); Location l = chunk.getBlock(8, 0, 0).getLocation();
Biome b = TerraBiomeGrid.fromWorld(world).getBiome(l.getBlockX(), l.getBlockZ()); Biome b = TerraBiomeGrid.fromWorld(world).getBiome(l.getBlockX(), l.getBlockZ(), GenerationPhase.POPULATE);
for(Map.Entry<OreConfig, Range> e : BiomeConfig.fromBiome((UserDefinedBiome) b).getOres().entrySet()) { for(Map.Entry<OreConfig, Range> e : BiomeConfig.fromBiome((UserDefinedBiome) b).getOres().entrySet()) {
int num = e.getValue().get(random); int num = e.getValue().get(random);
for(int i = 0; i < num; i++) { for(int i = 0; i < num; i++) {

View File

@ -9,6 +9,7 @@ import org.bukkit.Location;
import org.bukkit.World; import org.bukkit.World;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.polydev.gaea.biome.Biome; import org.polydev.gaea.biome.Biome;
import org.polydev.gaea.generation.GenerationPhase;
import org.polydev.gaea.population.GaeaBlockPopulator; import org.polydev.gaea.population.GaeaBlockPopulator;
import org.polydev.gaea.profiler.ProfileFuture; import org.polydev.gaea.profiler.ProfileFuture;
import org.polydev.gaea.util.WorldUtil; import org.polydev.gaea.util.WorldUtil;
@ -22,14 +23,14 @@ public class TreePopulator extends GaeaBlockPopulator {
int x = random.nextInt(16); // Decrease chances of chunk-crossing trees int x = random.nextInt(16); // Decrease chances of chunk-crossing trees
int z = random.nextInt(16); int z = random.nextInt(16);
Location origin = chunk.getBlock(x, 0, z).getLocation(); Location origin = chunk.getBlock(x, 0, z).getLocation();
Biome b = TerraBiomeGrid.fromWorld(world).getBiome(origin); Biome b = TerraBiomeGrid.fromWorld(world).getBiome(origin, GenerationPhase.POPULATE);
if(((UserDefinedDecorator) b.getDecorator()).getTreeChance() < random.nextInt(100)) return; if(((UserDefinedDecorator) b.getDecorator()).getTreeChance() < random.nextInt(100)) return;
int numTrees = 0; int numTrees = 0;
for(int i = 0; i < 48; i++) { for(int i = 0; i < 48; i++) {
int y = WorldUtil.getHighestValidSpawnAt(chunk, x, z); int y = WorldUtil.getHighestValidSpawnAt(chunk, x, z);
if(y <= 0) continue; if(y <= 0) continue;
origin = chunk.getBlock(x, y, z).getLocation().add(0, 1, 0); origin = chunk.getBlock(x, y, z).getLocation().add(0, 1, 0);
b = TerraBiomeGrid.fromWorld(world).getBiome(origin); b = TerraBiomeGrid.fromWorld(world).getBiome(origin, GenerationPhase.POPULATE);
numTrees++; numTrees++;
try { try {
b.getDecorator().getTrees().get(random).plant(origin, random, false, Terra.getInstance()); b.getDecorator().getTrees().get(random).plant(origin, random, false, Terra.getInstance());