World mapper and biome generator reworked;

World mapper now produces normalized values.

The world height shader uses the values to decide on heights.

The current biome generator was reworked (untested).

Also renamed (refactoring) the mapper.
This commit is contained in:
Harrison Deng 2020-04-22 21:15:37 -05:00
parent 4ed9d4d272
commit 7d07332ef5
6 changed files with 116 additions and 58 deletions

View File

@ -1,5 +1,6 @@
package ca.recrown.islandsurvivalcraft.world;
import java.security.InvalidAlgorithmParameterException;
import java.util.Random;
import org.bukkit.Material;
@ -21,20 +22,23 @@ public class IslandWorldGenerator {
private final BiomeSelector biomeSelector;
private final IslandBiomeGenerator biomeGenerator;
private final WorldHeightShader heightShader;
public final World world;
public final IslandWorldBaseTerrainMap islandMapGenerator;
public final IslandWorldMapper islandMapGenerator;
public final Random random;
public IslandWorldGenerator(World world, IslandBiomeGenerator islandBiomeGenerator, Random random) {
this.world = world;
this.maxHeight = world.getMaxHeight();
this.random = random;
this.biomeSelector = new BiomeSelector(random);
this.bedrockGenerator = new BedrockGenerator(random);
this.islandMapGenerator = new IslandWorldBaseTerrainMap(random, world.getSeaLevel(), 4);
this.islandMapGenerator = new IslandWorldMapper(random);
this.biomeGenerator = islandBiomeGenerator;
this.heightShader = new WorldHeightShader(world.getSeed(), islandMapGenerator);
int bedrockMaxHeight = 5;
int bedrockMinHeight = 1;
this.bedrockGenerator = new BedrockGenerator(random, bedrockMaxHeight, bedrockMinHeight);
this.heightShader = new WorldHeightShader(world.getSeed(), islandMapGenerator, world.getSeaLevel(), maxHeight,
bedrockMaxHeight + 2);
biomeSelector.initialize();
islandBiomeGenerator.initialize(world, islandMapGenerator, biomeSelector);
}
@ -42,21 +46,27 @@ public class IslandWorldGenerator {
public void GenerateChunk(int chunkX, int chunkZ, int localX, int localZ, ChunkData chunk, BiomeGrid biomeGrid) {
int worldX = 16 * chunkX + localX;
int worldZ = 16 * chunkZ + localZ;
//gets the bedrock.
// gets the bedrock.
int bedrockHeight = bedrockGenerator.getBedrockHeight(worldX, worldZ);
//Sets the biome.
// Sets the biome.
Biome currentBiome = biomeGenerator.GenerateBiome(chunkX, chunkZ, localX, localZ);
for (int y = 0; y < maxHeight; y++) {
biomeGrid.setBiome(localX, y, localZ, currentBiome);
}
//get height shader.
int height = heightShader.getAltitude(worldX, worldZ, currentBiome);
}
// get height shader.
int height;
try {
height = heightShader.getAltitude(worldX, worldZ, currentBiome);
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
height = maxHeight;
}
//set general shape
chunk.setRegion(localX, 0, localZ, localX, height, localZ, Material.BEDROCK);
chunk.setRegion(localX, 0, localZ, localX, height, localZ, Material.STONE);
//set bedrock last
chunk.setRegion(localX, 0, localZ, localX, bedrockHeight, localZ, Material.BEDROCK);

View File

@ -7,27 +7,20 @@ import org.bukkit.util.noise.SimplexOctaveGenerator;
import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateValidatable;
import ca.recrown.islandsurvivalcraft.pathfinding.DepthFirstSearch;
public class IslandWorldBaseTerrainMap implements CoordinateValidatable {
public class IslandWorldMapper implements CoordinateValidatable {
private SimplexOctaveGenerator noiseGenerator;
private final int noiseOctaves = 8;
private final int islandGenerationPercent = 47;
private final float islandGenerationPercent = 30;
private final float exaggerationFactor = 1.5f;
private final double noiseFrequency = 0.5D;
private final double noiseAmplitude = 0.5D;
private final float shoreFactor = 0.065f;
private final int maxHeightAboveSea = 8;
private final int lowestSeaFloor = 16;
private int seaLevel;
private int shallowDepth;
private final float shallowPortion = 0.06f;
private final DepthFirstSearch dfs;
public IslandWorldBaseTerrainMap(Random random, int seaLevel, int shallowDepth) {
public IslandWorldMapper(Random random) {
dfs = new DepthFirstSearch(this);
this.seaLevel = seaLevel;
this.shallowDepth = shallowDepth;
this.noiseGenerator = new SimplexOctaveGenerator(random, noiseOctaves);
this.seaLevel = seaLevel;
this.shallowDepth = shallowDepth;
}
/**
@ -96,37 +89,37 @@ public class IslandWorldBaseTerrainMap implements CoordinateValidatable {
* @return true if it is considered the shallow portion.
*/
public boolean isShallowPortion(int worldX, int worldZ) {
if (getIslandValue(worldX, worldZ) >= - (float) shallowDepth / (float) seaLevel) {
if (getIslandValue(worldX, worldZ) >= -shallowPortion) {
return true;
}
return true;
}
private double getTerrainValue(int worldX, int worldZ) {
double normalized = getNoiseValue(worldX, worldZ);
return Math.pow(normalized, exaggerationFactor) * (islandGenerationPercent + seaLevel);
}
public double getNoiseValue(int worldX, int worldZ) {
return (noiseGenerator.noise(worldX, worldZ, noiseFrequency, noiseAmplitude, true) + 1D) / 2D;
}
/***
/**
* Island value will be 0 or positive if it is a part of an island.
* If less than, it is considered under the sea.
* Does not factor in a shallow depth.
* The value above zero is normalized to [0, 1].
* The value is normalized to [0, 1].
* @param worldX the x world coordinate to obtain this value for.
* @param worldZ the z world coordinate to obtain this value for.
* @return a value representing the island at the given point.
*/
public float getIslandValue(int worldX, int worldZ) {
return (float) ((getTerrainValue(worldX, worldZ) - seaLevel) / (islandGenerationPercent));
public double getIslandValue(int worldX, int worldZ) {
double normalized = getNoiseValue(worldX, worldZ);
return Math.pow(normalized, exaggerationFactor);
}
public int getBaseTerrainHeight(int worldX, int worldZ) {
double terrainVal = getTerrainValue(worldX, worldZ);
return (int) Math.max(Math.min(terrainVal, maxHeightAboveSea + seaLevel), lowestSeaFloor);
public double getNoiseValue(int worldX, int worldZ) {
float portionSea = 1f - (this.islandGenerationPercent / 100f);
float shift = 1f - 2 * portionSea;
double noise = (noiseGenerator.noise(worldX, worldZ, noiseFrequency, noiseAmplitude, true) + shift);
float maxNeg = -1 + shift;
float maxPos = 1 + shift;
if (noise < 0) {
return noise / maxNeg;
}
return noise / maxPos;
}
/**
@ -142,7 +135,7 @@ public class IslandWorldBaseTerrainMap implements CoordinateValidatable {
if (!isIsland(firstBlockX, firstBlockZ)) return false;
if (!isIsland(secondBlockX, secondBlockZ)) return false;
dfs.setStartPosition(firstBlockX, firstBlockZ);
dfs.setEndPosition(secondBlockX, secondBlockZ);
dfs.setDirectionPosition(secondBlockX, secondBlockZ);
boolean res = dfs.buildPathToEndNode();
dfs.deleteTree();
return res;

View File

@ -6,12 +6,14 @@ import org.bukkit.util.noise.SimplexOctaveGenerator;
public class BedrockGenerator {
private SimplexOctaveGenerator noiseGenerator;
private final int maxBedrockHeight = 5;
private final int minBedrockHeight = 1;
private final int maxBedrockHeight;
private final int minBedrockHeight;
public BedrockGenerator(Random random) {
public BedrockGenerator(Random random, int maxBedrockHeight, int minBedrockHeight) {
noiseGenerator = new SimplexOctaveGenerator(random, 8);
noiseGenerator.setScale(0.1D);
this.maxBedrockHeight = maxBedrockHeight;
this.minBedrockHeight = minBedrockHeight;
}
private double getNormalizedNoise(int worldX, int worldZ) {

View File

@ -1,16 +1,18 @@
package ca.recrown.islandsurvivalcraft.world.generation;
import java.util.Arrays;
import org.bukkit.World;
import org.bukkit.block.Biome;
import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateTargetValidatable;
import ca.recrown.islandsurvivalcraft.pathfinding.DepthFirstSearch;
import ca.recrown.islandsurvivalcraft.world.BiomeSelector;
import ca.recrown.islandsurvivalcraft.world.IslandWorldBaseTerrainMap;
import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
public class BiomePerIslandGenerator implements IslandBiomeGenerator, CoordinateTargetValidatable {
private boolean initialized;
private IslandWorldBaseTerrainMap islandLocator;
private IslandWorldMapper islandLocator;
private BiomeSelector biomeSelector;
private TemperatureMapGenerator temperatureMapGenerator;
private World world;
@ -19,29 +21,51 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator, Coordinate
private Biome shoreBiome;
private float temperature;
private int chunkX, chunkZ;
private Biome[][] chunkCache;
public BiomePerIslandGenerator() {
this.temperatureMapGenerator = new TemperatureMapGenerator();
chunkCache = new Biome[16][16];
}
@Override
public void initialize(World world, IslandWorldBaseTerrainMap mapGenerator, BiomeSelector biomeSelector) {
public void initialize(World world, IslandWorldMapper mapGenerator, BiomeSelector biomeSelector) {
if (initialized) throw new IllegalStateException("Biome generator already initialized.");
initialized = true;
this.islandLocator = mapGenerator;
this.biomeSelector = biomeSelector;
this.world = world;
this.temperatureMapGenerator.setSeed(world.getSeed());
dfs = new DepthFirstSearch(islandLocator);
dfs = new DepthFirstSearch(32, islandLocator);
}
@Override
public boolean isCoordinateTarget(int x, int y) {
//Should be ran for a island until finding biome info or until it looked for it's entirety and couldn't find it. This is one island.
//First check if the chunk we're looking at is generated.
if (world.isChunkGenerated(x/16, y/16)) {
//If so, we know that the biomes have already been assigned, and can therefore use it.
Biome foundBiome = world.getBiome(x, 0, y);
if (islandLocator.isShore(x, y)) {
shoreBiome = foundBiome;
this.shoreBiome = foundBiome;
} else {
mainBiome = foundBiome;
this.mainBiome = foundBiome;
}
if (mainBiome != null && shoreBiome != null) {
return true;
}
} else
//If the same chunk, then do the same thing, except use the chunk biome cache instead to look for it.
if (x/16 == chunkX && y/16 == chunkZ) {
Biome foundBiome = chunkCache[Math.abs(x % 16)][Math.abs(y % 16)];
if (foundBiome == null) return false;
if (islandLocator.isShore(x, y)) {
this.shoreBiome = foundBiome;
} else {
this.mainBiome = foundBiome;
}
if (mainBiome != null && shoreBiome != null) {
return true;
@ -50,11 +74,21 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator, Coordinate
return false;
}
@Override
public Biome GenerateBiome(int chunkX, int chunkZ, int localX, int localZ) {
int worldX = chunkX * 16 + localX;
int worldZ = chunkZ * 16 + localZ;
//Clear chunk cache if different chunk
if (chunkX != this.chunkX || chunkZ != this.chunkZ) {
this.chunkX = chunkX;
this.chunkZ = chunkZ;
for (int i = 0; i < chunkCache.length; i++) {
Arrays.fill(chunkCache[i], null);
}
}
if (islandLocator.isIsland(worldX, worldZ)) {
if (mainBiome == null && shoreBiome == null) {
dfs.setStartPosition(worldX, worldZ);
@ -76,6 +110,8 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator, Coordinate
} else if (islandLocator.isShore(worldX, worldZ)) {
designatedBiome = shoreBiome;
}
chunkCache[localX][localZ] = designatedBiome;
return designatedBiome;
}

View File

@ -4,10 +4,10 @@ import org.bukkit.World;
import org.bukkit.block.Biome;
import ca.recrown.islandsurvivalcraft.world.BiomeSelector;
import ca.recrown.islandsurvivalcraft.world.IslandWorldBaseTerrainMap;
import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
public interface IslandBiomeGenerator {
public void initialize(World world, IslandWorldBaseTerrainMap mapGenerator, BiomeSelector biomeSelector);
public void initialize(World world, IslandWorldMapper mapGenerator, BiomeSelector biomeSelector);
public Biome GenerateBiome(int chunkX, int chunkZ, int localX, int localZ);
public IslandBiomeGenerator getInstance();
}

View File

@ -1,28 +1,35 @@
package ca.recrown.islandsurvivalcraft.world.shaders;
import java.security.InvalidAlgorithmParameterException;
import java.util.Random;
import org.bukkit.block.Biome;
import org.bukkit.util.noise.SimplexOctaveGenerator;
import ca.recrown.islandsurvivalcraft.world.IslandWorldBaseTerrainMap;
import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
public class WorldHeightShader {
private Random random;
private SimplexOctaveGenerator noiseGenerator;
private IslandWorldBaseTerrainMap islandLocator;
private IslandWorldMapper islandLocator;
private int seaLevel;
private int worldHeight;
private int minimumHeight;
private final float probabilityOfAdditive = 0.75f;
public WorldHeightShader(long seed, IslandWorldBaseTerrainMap islandLocator) {
public WorldHeightShader(long seed, IslandWorldMapper islandLocator, int seaLevel, int worldHeight, int minimumHeight) {
random = new Random(seed);
noiseGenerator = new SimplexOctaveGenerator(random, 2);
noiseGenerator.setScale(0.05D);
this.islandLocator = islandLocator;
this.seaLevel = seaLevel;
this.worldHeight = worldHeight;
this.minimumHeight = minimumHeight;
}
public int getAltitude(int worldX, int worldZ, Biome biome) {
public int getAltitude(int worldX, int worldZ, Biome biome) throws InvalidAlgorithmParameterException {
double modifier = getHeightModifier(worldX, worldZ);
int baseValue = islandLocator.getBaseTerrainHeight(worldX, worldZ);
int baseValue = calculateTerrainHeight(worldX, worldZ);
int height = 0;
String biomeName = biome.name();
if (biomeName.contains("hills")) {
@ -42,9 +49,19 @@ public class WorldHeightShader {
} else {
height = (int) (getNormalizedHeightModifier(worldX, worldZ) * 10D + baseValue);
}
if (height > worldHeight) throw new InvalidAlgorithmParameterException("Resulting height is greater than world height!!!");
return height;
}
private int calculateTerrainHeight(int worldX, int worldZ) {
double islandValue = islandLocator.getIslandValue(worldX, worldZ);
if (islandValue >= 0) {
return seaLevel;
}
return (int) Math.max(-islandValue * seaLevel, minimumHeight);
}
private double getHeightModifier(int worldX, int worldZ) {
return (2 * probabilityOfAdditive - noiseGenerator.noise(worldX, worldZ, 0.5D, 0.5D, true));
}