World generation feels more natural now.
Mountain tips after a certain height is stone. Deep ocean biomes are now actually deeper. Temperature map no longer calculates in groups of 4.
This commit is contained in:
parent
085827264b
commit
740ca812ae
@ -20,6 +20,7 @@ public class IslandWorldMapper implements CoordinateValidatable {
|
||||
private final float shoreFactor = 0.095f;
|
||||
private final float shallowPortion = 0.07f;
|
||||
private final double scale = 0.005D;
|
||||
private final double deepOceanPortion = 0.6d;
|
||||
private final DepthFirstSearch dfs;
|
||||
|
||||
public IslandWorldMapper(long seed, Cache<Point2, Double> blockValueCache) {
|
||||
@ -150,6 +151,10 @@ public class IslandWorldMapper implements CoordinateValidatable {
|
||||
return res;
|
||||
}
|
||||
|
||||
public boolean isDeepOcean(int worldX, int worldZ) {
|
||||
return getWorldValue(worldX, worldZ) <= -deepOceanPortion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(int x, int y) {
|
||||
return isIsland(x, y);
|
||||
|
@ -1,7 +1,5 @@
|
||||
package ca.recrown.islandsurvivalcraft.world.generation;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
|
||||
@ -28,7 +26,7 @@ public interface BiomeGenerator {
|
||||
* @param tempGenerator The temperature generator to be used.
|
||||
* @param biomeCache Cache for biomes.
|
||||
* @param chunkGenCache Cache for whether or not the chunk is generated.
|
||||
* @param random A random that bukkit passes.
|
||||
* @param seed The seed to use for the biome for this column.
|
||||
*/
|
||||
public void generateBiomeColumn(Biome[][][] chunkBiomeSets, World world, int chunkX, int chunkZ, int localX, int localZ, IslandWorldMapper mapper, BiomeSelector biomeSelector, TemperatureMapGenerator tempGenerator, Cache<Point2, Biome[]> biomeCache, Cache<Point2, Boolean> chunkGenCache, Random random);
|
||||
public void generateBiomeColumn(Biome[][][] chunkBiomeSets, World world, int chunkX, int chunkZ, int localX, int localZ, IslandWorldMapper mapper, BiomeSelector biomeSelector, TemperatureMapGenerator tempGenerator, Cache<Point2, Biome[]> biomeCache, Cache<Point2, Boolean> chunkGenCache, long seed);
|
||||
}
|
@ -72,7 +72,7 @@ public class IslandWorldChunkGenerator extends ChunkGenerator implements Listene
|
||||
final int worldZ = Utilities.CHUNK_SIZE * chunkZ + localZ;
|
||||
if (biomeSet[localX][localZ][0] == null) {
|
||||
biomeGenerator.generateBiomeColumn(biomeSet, world, chunkX, chunkZ, localX, localZ, mapper, biomeSelector,
|
||||
temperatureMapGenerator, biomeCache, chunkExistenceCache, new Random(seed + worldX + worldZ));
|
||||
temperatureMapGenerator, biomeCache, chunkExistenceCache, seed);
|
||||
}
|
||||
if (biomeSet[localX][localZ][0] == null) throw new IllegalStateException("Biome was null.");
|
||||
tasks.add(exBeta.submit(() -> {
|
||||
@ -87,11 +87,11 @@ public class IslandWorldChunkGenerator extends ChunkGenerator implements Listene
|
||||
int currentTerrainHeight = terrainHeight - 1;
|
||||
int bedrockHeight = random.nextInt(5) + 1;
|
||||
if (layerShader.hasSpecialLayers(currentBiomeSet[0])) {
|
||||
BlockData currentMaterial = layerShader.getMaterialForHeight(worldX, worldZ, currentTerrainHeight, terrainHeight - 1, currentBiomeSet[0]);
|
||||
BlockData currentMaterial = layerShader.getMaterialForHeight(worldX, worldZ, currentTerrainHeight, terrainHeight - 1, currentBiomeSet);
|
||||
while (currentMaterial != null) {
|
||||
chunkData.setBlock(localX, currentTerrainHeight, localZ, currentMaterial);
|
||||
currentTerrainHeight--;
|
||||
currentMaterial = layerShader.getMaterialForHeight(worldX, worldZ, currentTerrainHeight, terrainHeight - 1, currentBiomeSet[0]);
|
||||
currentMaterial = layerShader.getMaterialForHeight(worldX, worldZ, currentTerrainHeight, terrainHeight - 1, currentBiomeSet);
|
||||
}
|
||||
} else {
|
||||
int surfaceThickness = layerShader.getSurfaceThickness(worldX, worldZ, currentBiomeSet[0]);
|
||||
|
@ -29,7 +29,7 @@ class TemperatureMapGenerator {
|
||||
}
|
||||
|
||||
public float getTemperature(int worldX, int worldZ) {
|
||||
Point2 loc = new Point2(worldX/4, worldZ/4);
|
||||
Point2 loc = new Point2(worldX, worldZ);
|
||||
Float val = temperatureCache.get(loc);
|
||||
if (val == null) {
|
||||
val = (float) noiseGenerator.noise(worldX, worldZ, frequency, amplitude, true);
|
||||
|
@ -1,7 +1,5 @@
|
||||
package ca.recrown.islandsurvivalcraft.world.generation;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
|
||||
@ -17,7 +15,7 @@ import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
|
||||
|
||||
public class UniBiomeIslandGenerator implements BiomeGenerator {
|
||||
@Override
|
||||
public void generateBiomeColumn(Biome[][][] chunkBiomeSets, World world, int chunkX, int chunkZ, int localX, int localZ, IslandWorldMapper mapper, BiomeSelector biomeSelector, TemperatureMapGenerator tempGen, Cache<Point2, Biome[]> biomeCache, Cache<Point2, Boolean> chunkGenCache, Random random) {
|
||||
public void generateBiomeColumn(Biome[][][] chunkBiomeSets, World world, int chunkX, int chunkZ, int localX, int localZ, IslandWorldMapper mapper, BiomeSelector biomeSelector, TemperatureMapGenerator tempGen, Cache<Point2, Biome[]> biomeCache, Cache<Point2, Boolean> chunkGenCache, long seed) {
|
||||
int worldX = 16 * chunkX + localX;
|
||||
int worldZ = 16 * chunkZ + localZ;
|
||||
Point2 chunkCoords = Utilities.worldToChunkCoordinates(new Point2(worldX, worldZ));
|
||||
@ -33,7 +31,7 @@ public class UniBiomeIslandGenerator implements BiomeGenerator {
|
||||
//Fine, check if it's ocean.
|
||||
if (!mapper.isIsland(worldX, worldZ)) {
|
||||
biomeSet = chunkBiomeSets[localX][localZ];
|
||||
biomeSet[0] = biomeSelector.getOceanBiome(tempGen.getTemperature(worldX, worldZ), random);
|
||||
biomeSet[0] = biomeSelector.getOceanBiome(tempGen.getTemperature(worldX, worldZ), mapper.isDeepOcean(worldX, worldZ), seed + worldX + worldZ);
|
||||
setCacheBiome(worldX, worldZ, biomeSet, chunkBiomeSets, biomeCache);
|
||||
return;
|
||||
}
|
||||
@ -46,9 +44,9 @@ public class UniBiomeIslandGenerator implements BiomeGenerator {
|
||||
search.setStartPosition(worldX, worldZ);
|
||||
if (!search.findTarget(islandInfo)) {
|
||||
float temp = tempGen.getTemperature(worldX, worldZ);
|
||||
if (islandInfo.main == null) islandInfo.main = biomeSelector.getLandBiome(temp, random);
|
||||
if (islandInfo.main == null) islandInfo.main = biomeSelector.getLandBiome(temp, seed + worldX + worldZ);
|
||||
if (islandInfo.shore == null) islandInfo.shore = biomeSelector.getShoreBiome(islandInfo.main, temp);
|
||||
if (islandInfo.shallow == null) islandInfo.shallow = biomeSelector.getOceanBiome(temp, random);
|
||||
if (islandInfo.shallow == null) islandInfo.shallow = biomeSelector.getOceanBiome(temp, mapper.isDeepOcean(worldX, worldZ), seed + worldX + worldZ);
|
||||
}
|
||||
|
||||
PropagatorInfo propInfo = new PropagatorInfo(islandInfo, chunkBiomeSets, new Point2(chunkX, chunkZ), mapper, biomeCache);
|
||||
|
@ -27,13 +27,11 @@ public class WorldHeightShader {
|
||||
int height = 0;
|
||||
String biomeName = biomeSet[0].name().toLowerCase();
|
||||
if (!mapper.isLand(worldX, worldZ)) {
|
||||
if (biomeName.contains("deep")) {
|
||||
height = (int) calculateTerrainFactor(worldX, worldZ, seaLevel * 0.9d, 1.5d, 0.5d, 1d, 0.125d);
|
||||
} else {
|
||||
height = (int) calculateTerrainFactor(worldX, worldZ, seaLevel * 0.7d, 1.5d, 0.5d, 1d, 0.15d);
|
||||
}
|
||||
} else if (biomeName.contains("shore") || biomeName.contains("beach")) {
|
||||
height = (int) calculateTerrainFactor(worldX, worldZ, seaLevel * 0.8d, 1.7d, 0.5d, 1d, 0.1d);
|
||||
} else if (biomeName.contains("beach")) {
|
||||
height = (int) (getIslandBiomeHeight(worldX, worldZ, biomeSet[1], 5d));
|
||||
} else if (biomeName.contains("shore")) {
|
||||
height = (int) (getIslandBiomeHeight(worldX, worldZ, biomeSet[1], 20d));
|
||||
} else {
|
||||
height = getIslandBiomeHeight(worldX, worldZ, biomeSet[0], 0d) + 1;
|
||||
}
|
||||
|
@ -6,14 +6,19 @@ import org.bukkit.Material;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.Snowable;
|
||||
import org.bukkit.util.noise.SimplexOctaveGenerator;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.caching.Cache;
|
||||
import ca.recrown.islandsurvivalcraft.datatypes.Vector3;
|
||||
import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
|
||||
|
||||
public class WorldLayerShader {
|
||||
private final Cache<Vector3, Double> noiseCache = new Cache<>(256);
|
||||
private final SimplexOctaveGenerator noise;
|
||||
private final IslandWorldMapper mapper;
|
||||
private final long seed;
|
||||
private final int seaLevel, maxHeight;
|
||||
private final Random random = new Random();
|
||||
private final Random random;
|
||||
|
||||
|
||||
private final Material[] badlandTerrocota = new Material[] {
|
||||
@ -30,13 +35,17 @@ public class WorldLayerShader {
|
||||
this.seed = seed;
|
||||
this.seaLevel = seaLevel;
|
||||
this.maxHeight = maxHeight;
|
||||
random = new Random(seed);
|
||||
this.noise = new SimplexOctaveGenerator(new Random(seed), 4);
|
||||
this.noise.setScale(0.03d);
|
||||
}
|
||||
|
||||
public boolean hasSpecialLayers(Biome biome) {
|
||||
String biomeName = biome.toString().toLowerCase();
|
||||
return
|
||||
biomeName.contains("badlands") ||
|
||||
biomeName.contains("snowy");
|
||||
biomeName.contains("snowy") ||
|
||||
biomeName.contains("mountain");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -45,12 +54,15 @@ public class WorldLayerShader {
|
||||
* @param worldX The world x coordinate.
|
||||
* @param worldZ The world z coordinate.
|
||||
* @param y The elevation.
|
||||
* @param biome the associated biome.
|
||||
* @param biomeSet the associated biome.
|
||||
* @return the material for the layer. Returning null means no more special layers.
|
||||
*/
|
||||
public BlockData getMaterialForHeight(int worldX, int worldZ, int y, int highestPoint, Biome biome) {
|
||||
String biomeName = biome.toString().toLowerCase();
|
||||
if (biomeName.contains("badlands")) {
|
||||
public BlockData getMaterialForHeight(int worldX, int worldZ, int y, int highestPoint, Biome[] biomeSet) {
|
||||
Biome mainBiome = biomeSet[0];
|
||||
String mainBiomeName = mainBiome.toString().toLowerCase();
|
||||
BlockData res = null;
|
||||
|
||||
if (mainBiomeName.contains("badlands")) {
|
||||
int seedOffset = (worldX + worldZ) / 1000;
|
||||
random.setSeed(seed + seedOffset);
|
||||
int yInterval = (int) (random.nextFloat() * 9) + 1;
|
||||
@ -64,27 +76,36 @@ public class WorldLayerShader {
|
||||
for (int i = 0; i < selected.length; i++) {
|
||||
selected[i] = badlandTerrocota[random.nextInt(amountOfLayers)];
|
||||
}
|
||||
return selected[subLayer].createBlockData();
|
||||
res = selected[subLayer].createBlockData();
|
||||
} else {
|
||||
return Material.TERRACOTTA.createBlockData();
|
||||
res = Material.TERRACOTTA.createBlockData();
|
||||
}
|
||||
}
|
||||
} else if (biomeName.contains("snowy")) {
|
||||
} else if (mainBiomeName.contains("mountain")) {
|
||||
if (y > (maxHeight * 0.48d) - getNormalizedNoise(worldX, worldZ, 2f) * 6d) {
|
||||
res = Material.STONE.createBlockData();
|
||||
} else if (y > highestPoint - getSurfaceThickness(worldX, worldZ, mainBiome)) {
|
||||
res = getSurfaceMaterial(mainBiome).createBlockData();
|
||||
} else if (y > highestPoint - getSurfaceThickness(worldX, worldZ, mainBiome) - getTransitionMaterialThickness(worldX, worldZ, mainBiome)) {
|
||||
res = getTransitionMaterial(mainBiome).createBlockData();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (mainBiomeName.contains("snowy")) {
|
||||
if (highestPoint == y) {
|
||||
return Material.SNOW.createBlockData();
|
||||
} else if (y > highestPoint - 1 - getSurfaceThickness(worldX, worldZ, biome)) {
|
||||
Material surfaceMaterial = getSurfaceMaterial(biome);
|
||||
BlockData res = surfaceMaterial.createBlockData();
|
||||
if (getSurfaceMaterial(biome) == Material.GRASS_BLOCK) {
|
||||
res = Material.SNOW.createBlockData();
|
||||
} else if (y > highestPoint - 1 - getSurfaceThickness(worldX, worldZ, mainBiome)) {
|
||||
if (res == null || res.getMaterial() == Material.DIRT) res = getSurfaceMaterial(mainBiome).createBlockData();
|
||||
if (res.getMaterial() == Material.GRASS_BLOCK) {
|
||||
Snowable snowable = (Snowable) res;
|
||||
snowable.setSnowy(true);
|
||||
}
|
||||
return res;
|
||||
} else if (y > highestPoint - 1 - getSurfaceThickness(worldX, worldZ, biome) - getTransitionMaterialThickness(worldX, worldZ, biome)) {
|
||||
return getTransitionMaterial(biome).createBlockData();
|
||||
} else if (y > highestPoint - 1 - getSurfaceThickness(worldX, worldZ, mainBiome) - getTransitionMaterialThickness(worldX, worldZ, mainBiome)) {
|
||||
if (res == null) res = getTransitionMaterial(mainBiome).createBlockData();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return res;
|
||||
}
|
||||
|
||||
public Material getSurfaceMaterial(Biome biome) {
|
||||
@ -105,7 +126,7 @@ public class WorldLayerShader {
|
||||
String biomeName = biome.toString().toLowerCase();
|
||||
|
||||
if (biomeName.contains("beach")) {
|
||||
return (int) (Math.abs(mapper.getWorldValue(worldX, worldZ)) * 5) + 4;
|
||||
return (int) (getNormalizedNoise(worldX, worldZ, 1.3f) * 5) + 4;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -115,7 +136,7 @@ public class WorldLayerShader {
|
||||
String biomeName = biome.toString().toLowerCase();
|
||||
if (biomeName.contains("beach") || biomeName.contains("desert")) {
|
||||
return Material.SANDSTONE;
|
||||
} else if (biomeName.contains("stone")) {
|
||||
} else if (biome == Biome.STONE_SHORE) {
|
||||
return Material.STONE;
|
||||
} else if (biomeName.contains("ocean")) {
|
||||
return Material.GRAVEL;
|
||||
@ -124,6 +145,16 @@ public class WorldLayerShader {
|
||||
}
|
||||
|
||||
public int getTransitionMaterialThickness(int worldX, int worldZ, Biome biome) {
|
||||
return (int) (Math.abs(mapper.getWorldValue(worldX, worldZ)) * 4) + 4;
|
||||
return (int) (getNormalizedNoise(worldX, worldZ, 1.3f) * 4) + 4;
|
||||
}
|
||||
|
||||
private double getNormalizedNoise(int worldX, int worldZ, float freq) {
|
||||
Vector3 key = new Vector3(worldX, worldZ, freq);
|
||||
Double res = noiseCache.get(key);
|
||||
if (res == null) {
|
||||
res = (noise.noise(worldX, worldZ, freq, 0.5d) + 1d) / 2d;
|
||||
noiseCache.set(key, res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ package ca.recrown.islandsurvivalcraft.world.generation;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
@ -33,7 +32,6 @@ public class UniBiomeIslandGeneratorTest {
|
||||
private volatile Cache<Point2, Biome[]> biomeCache;
|
||||
private volatile Cache<Point2, Boolean> chunkExistenceCache;
|
||||
private final BiomeSelector biomeSelector = new BiomeSelector();
|
||||
private final Random random = new Random(SEED);
|
||||
|
||||
|
||||
private class BiomeGenTask implements Runnable {
|
||||
@ -63,7 +61,7 @@ public class UniBiomeIslandGeneratorTest {
|
||||
for (int localZ = 0; localZ < Utilities.CHUNK_SIZE; localZ++) {
|
||||
if (biomes[localX][localZ] == null) {
|
||||
biomeGenerator.generateBiomeColumn(biomes, dummyWorld, chunkX, chunkZ, localX, localZ, mapper,
|
||||
biomeSelector, temperatureMapGenerator, biomeCache, chunkExistenceCache, random);
|
||||
biomeSelector, temperatureMapGenerator, biomeCache, chunkExistenceCache, SEED);
|
||||
}
|
||||
if (biomes[localX][localZ] == null)
|
||||
throw new IllegalStateException("Biome was null.");
|
||||
|
Loading…
Reference in New Issue
Block a user