Now generating primitive islands.
Favourable island mapper values.
This commit is contained in:
parent
e4597538b2
commit
a6cebe703b
@ -11,7 +11,7 @@ import ca.recrown.islandsurvivalcraft.Utilities;
|
||||
|
||||
public class BiomeSelector {
|
||||
private boolean initialized = false;
|
||||
private final Random random;
|
||||
private volatile Random random;
|
||||
private final HashMap<Biome, Float> lands = new HashMap<>();
|
||||
private HashMap<Float, ArrayList<Biome>> temperaturesForLand = new HashMap<>();
|
||||
private final HashMap<Float, ArrayList<Biome>> temperaturePartitionedLandBiomes = new HashMap<>();
|
||||
@ -229,4 +229,8 @@ public class BiomeSelector {
|
||||
|
||||
return biomes.get((int) random.nextFloat() * biomes.size());
|
||||
}
|
||||
|
||||
public void setBiome(Random random) {
|
||||
this.random = random;
|
||||
}
|
||||
}
|
@ -14,14 +14,14 @@ public class IslandWorldMapper implements CoordinateValidatable {
|
||||
|
||||
private final SimplexOctaveGenerator noiseGenerator;
|
||||
private final int noiseOctaves = 8;
|
||||
private final float islandBlockGenerationPercent = 10;
|
||||
private final float islandBlockGenerationPercent = 15f;
|
||||
private final float exaggerationFactor = 1f;
|
||||
private final float islandExaggeration = 1.6f;
|
||||
private final double noiseFrequency = 1.8D;
|
||||
private final float islandExaggeration = 1f;
|
||||
private final double noiseFrequency = 1.7D;
|
||||
private final double noiseAmplitude = 0.5D;
|
||||
private final double scale = 0.01D;
|
||||
private final float shoreFactor = 0.035f;
|
||||
private final float shallowPortion = 0.015f;
|
||||
private final double scale = 0.004D;
|
||||
private final float shoreFactor = 0.05f;
|
||||
private final float shallowPortion = 0.01f;
|
||||
private final DepthFirstSearch dfs;
|
||||
|
||||
public IslandWorldMapper(long seed, Cache<Point2, Double> blockValueCache) {
|
||||
@ -32,7 +32,7 @@ public class IslandWorldMapper implements CoordinateValidatable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Considers this land if it is greater than sea level.
|
||||
* Considers this land if the world block value is greater than or equal to 0.
|
||||
* @param worldX the X coordinate of location in world.
|
||||
* @param worldZ the Z coordinate of location in world.
|
||||
* @return
|
||||
@ -116,17 +116,16 @@ public class IslandWorldMapper implements CoordinateValidatable {
|
||||
|
||||
Double res = blockValueCache.get(p);
|
||||
if (res == null) {
|
||||
double portionSea = 1f - (this.islandBlockGenerationPercent / 100f);
|
||||
double shift = (portionSea - 1d) / 2d;
|
||||
double shift = 2 * (islandBlockGenerationPercent / 100f);
|
||||
double rawNoise = noiseGenerator.noise(worldX, worldZ, noiseFrequency, noiseAmplitude, true);
|
||||
double noise = 0;
|
||||
if (rawNoise < 0) {
|
||||
noise = ( - Math.pow(- rawNoise, exaggerationFactor) + shift);
|
||||
noise = ( - Math.pow(- rawNoise, exaggerationFactor) - shift);
|
||||
} else {
|
||||
noise = Math.pow(rawNoise, islandExaggeration) + shift;
|
||||
noise = Math.pow(rawNoise, islandExaggeration) - shift;
|
||||
}
|
||||
double maxNeg = -1 + shift;
|
||||
double maxPos = 1 + shift;
|
||||
double maxNeg = -1 - shift;
|
||||
double maxPos = 1 - shift;
|
||||
|
||||
if (noise < 0) {
|
||||
res = - noise / maxNeg;
|
||||
|
@ -23,6 +23,7 @@ import ca.recrown.islandsurvivalcraft.datatypes.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.world.BiomeSelector;
|
||||
import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
|
||||
import ca.recrown.islandsurvivalcraft.world.shaders.WorldHeightShader;
|
||||
import ca.recrown.islandsurvivalcraft.world.shaders.WorldLayerShader;
|
||||
|
||||
public class IslandWorldChunkGenerator extends ChunkGenerator implements Listener {
|
||||
private final Cache<Point2, Double> blockValueCache = new Cache<>(131072);
|
||||
@ -41,13 +42,15 @@ public class IslandWorldChunkGenerator extends ChunkGenerator implements Listene
|
||||
public ChunkData generateChunkData(World world, Random random, int chunkX, int chunkZ, BiomeGrid biomeGrid) {
|
||||
this.currentWorld = world;
|
||||
long seed = world.getSeed();
|
||||
int maxHeight = world.getMaxHeight();
|
||||
int seaLevel = world.getSeaLevel();
|
||||
IslandWorldMapper mapper = new IslandWorldMapper(seed, blockValueCache);
|
||||
TemperatureMapGenerator temperatureMapGenerator = new TemperatureMapGenerator(seed);
|
||||
WorldHeightShader heightShader = new WorldHeightShader(mapper, world.getSeaLevel(), world.getMaxHeight(), 3);
|
||||
WorldLayerShader layerShader = new WorldLayerShader(seed, seaLevel, maxHeight, mapper);
|
||||
BiomeGenerator biomeGenerator = new UniBiomeIslandGenerator();
|
||||
biomeSelector.setBiome(random);
|
||||
|
||||
int maxHeight = world.getMaxHeight();
|
||||
int seaLevel = world.getSeaLevel();
|
||||
LinkedList<Future<Boolean>> tasks = new LinkedList<>();
|
||||
ChunkData chunkData = createChunkData(world);
|
||||
Future<Boolean> preLoader = exAlpha.submit(() -> {
|
||||
@ -79,11 +82,25 @@ public class IslandWorldChunkGenerator extends ChunkGenerator implements Listene
|
||||
|
||||
final int worldX = Utilities.CHUNK_SIZE * chunkX + localX;
|
||||
final int worldZ = Utilities.CHUNK_SIZE * chunkZ + localZ;
|
||||
int terrainHeight = heightShader.getTerrainHeight(worldX, worldZ, biomes[localX][localZ]);
|
||||
int bedrockHeight = random.nextInt(5);
|
||||
chunkData.setRegion(localX, bedrockHeight, localZ, localX + 1, terrainHeight, localZ + 1, Material.STONE);
|
||||
Biome currentBiome = biomes[localX][localZ];
|
||||
int terrainHeight = heightShader.getTerrainHeight(worldX, worldZ, currentBiome);
|
||||
int currentTerrainHeight = terrainHeight - 1;
|
||||
int bedrockHeight = random.nextInt(5) + 1;
|
||||
if (layerShader.hasSpecialLayers(currentBiome)) {
|
||||
Material currentMaterial = layerShader.getMaterialForHeight(worldX, worldZ, currentTerrainHeight, currentBiome);
|
||||
while (currentMaterial != null) {
|
||||
chunkData.setBlock(localX, currentTerrainHeight, localZ, currentMaterial);
|
||||
currentTerrainHeight --;
|
||||
}
|
||||
} else {
|
||||
chunkData.setBlock(localX, currentTerrainHeight, localZ, layerShader.getSurfaceMaterial(currentBiome));
|
||||
int transitionHeight = layerShader.getTransitionMaterialThickness(worldX, worldZ, currentBiome);
|
||||
currentTerrainHeight -= transitionHeight;
|
||||
chunkData.setRegion(localX, currentTerrainHeight, localZ, localX + 1, currentTerrainHeight + transitionHeight, localZ + 1, layerShader.getTransitionMaterial(currentBiome));
|
||||
}
|
||||
chunkData.setRegion(localX, bedrockHeight, localZ, localX + 1, currentTerrainHeight + 1, localZ + 1, Material.STONE);
|
||||
if (terrainHeight < seaLevel) {
|
||||
chunkData.setRegion(localX, terrainHeight, localZ, localX + 1, seaLevel, localZ + 1, Material.WATER);
|
||||
chunkData.setRegion(localX, currentTerrainHeight, localZ, localX + 1, seaLevel, localZ + 1, Material.WATER);
|
||||
}
|
||||
chunkData.setRegion(localX, 0, localZ, localX + 1, bedrockHeight, localZ + 1, Material.BEDROCK);
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
package ca.recrown.islandsurvivalcraft.world.generation.noise;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.util.noise.SimplexOctaveGenerator;
|
||||
|
||||
public class FractalOctaveGenerator {
|
||||
private final SimplexOctaveGenerator simplex;
|
||||
private final double amplitude = 0.5d;
|
||||
private final double frequency = 0.5d;
|
||||
private final double scale = 0.05D;
|
||||
|
||||
public FractalOctaveGenerator(Random random) {
|
||||
simplex = new SimplexOctaveGenerator(random, 4);
|
||||
simplex.setScale(scale);
|
||||
}
|
||||
|
||||
public double noise(int x, int y) {
|
||||
return 1d - Math.abs(simplex.noise(x, y, frequency, amplitude, true));
|
||||
}
|
||||
}
|
@ -23,9 +23,9 @@ public class WorldHeightShader {
|
||||
if (biomeName.contains("hills")) {
|
||||
height = calculateTerrainHeight(worldX, worldZ, 15);
|
||||
} else if (biomeName.contains("mountains")) {
|
||||
height = calculateTerrainHeight(worldX, worldZ, 30);
|
||||
height = calculateTerrainHeight(worldX, worldZ, 120);
|
||||
} else if (biomeName.contains("plateau")) {
|
||||
height = Math.min(calculateTerrainHeight(worldX, worldZ, 70), 40);
|
||||
height = Math.min(calculateTerrainHeight(worldX, worldZ, 50), 30);
|
||||
} else if (biomeName.contains("modified")) {
|
||||
height = calculateTerrainHeight(worldX, worldZ, 45);
|
||||
} else if (biomeName.contains("shattered")) {
|
||||
@ -44,9 +44,13 @@ public class WorldHeightShader {
|
||||
}
|
||||
|
||||
private int calculateTerrainHeight(int worldX, int worldZ, int multiplier) {
|
||||
int blockHeight = seaLevel;
|
||||
int blockHeight = 0;
|
||||
double islandValue = islandLocator.getWorldValue(worldX, worldZ);
|
||||
blockHeight += islandValue * multiplier;
|
||||
if (!islandLocator.isLand(worldX, worldZ)) {
|
||||
blockHeight = (int) ((1d + islandValue) * (double) multiplier);
|
||||
} else {
|
||||
blockHeight += seaLevel + islandValue * multiplier;
|
||||
}
|
||||
return blockHeight;
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
package ca.recrown.islandsurvivalcraft.world.shaders;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Biome;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
|
||||
|
||||
public class WorldLayerShader {
|
||||
private final IslandWorldMapper mapper;
|
||||
private final long seed;
|
||||
private final int seaLevel, maxHeight;
|
||||
private final Random random = new Random();
|
||||
|
||||
|
||||
private final Material[] badlandTerrocota = new Material[] {
|
||||
Material.BLACK_TERRACOTTA,
|
||||
Material.BROWN_TERRACOTTA,
|
||||
Material.GRAY_TERRACOTTA,
|
||||
Material.LIGHT_GRAY_TERRACOTTA,
|
||||
Material.YELLOW_TERRACOTTA,
|
||||
Material.WHITE_TERRACOTTA
|
||||
};
|
||||
|
||||
public WorldLayerShader(long seed, int seaLevel, int maxHeight, IslandWorldMapper mapper) {
|
||||
this.mapper = mapper;
|
||||
this.seed = seed;
|
||||
this.seaLevel = seaLevel;
|
||||
this.maxHeight = maxHeight;
|
||||
}
|
||||
|
||||
public boolean hasSpecialLayers(Biome biome) {
|
||||
String biomeName = biome.toString().toLowerCase();
|
||||
return
|
||||
biomeName.contains("badlands");
|
||||
}
|
||||
|
||||
/**
|
||||
* Figures out the type of material to be placed at a given world coordinate set.
|
||||
*
|
||||
* @param worldX The world x coordinate.
|
||||
* @param worldZ The world z coordinate.
|
||||
* @param y The elevation.
|
||||
* @param biome the associated biome.
|
||||
* @return the material for the layer. Returning null means no more special layers.
|
||||
*/
|
||||
public Material getMaterialForHeight(int worldX, int worldZ, int y, Biome biome) {
|
||||
String biomeName = biome.toString().toLowerCase();
|
||||
if (biomeName.contains("badlands")) {
|
||||
int seedOffset = (worldX + worldZ) / 1000;
|
||||
random.setSeed(seed + seedOffset);
|
||||
int yInterval = (int) (random.nextFloat() * 9) + 1;
|
||||
int yBandInitial = (int) (seaLevel + random.nextDouble() * 50);
|
||||
|
||||
if (y > yBandInitial) {
|
||||
int amountOfLayers = Math.min(Math.max(1, random.nextInt(badlandTerrocota.length)), 4);
|
||||
int subLayer = y % yInterval;
|
||||
if (subLayer < amountOfLayers) {
|
||||
Material[] selected = new Material[amountOfLayers];
|
||||
for (int i = 0; i < selected.length; i++) {
|
||||
selected[i] = badlandTerrocota[random.nextInt(amountOfLayers)];
|
||||
}
|
||||
return selected[subLayer];
|
||||
} else {
|
||||
return Material.TERRACOTTA;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Material getSurfaceMaterial(Biome biome) {
|
||||
String biomeName = biome.toString().toLowerCase();
|
||||
if (biomeName.contains("beach") || biomeName.contains("desert") || biomeName.contains("warm_ocean")) {
|
||||
return Material.SAND;
|
||||
} else if (biomeName.contains("deep_ocean")) {
|
||||
return Material.GRAVEL;
|
||||
} else if (biomeName.contains("stone")) {
|
||||
return Material.STONE;
|
||||
}
|
||||
|
||||
return Material.GRASS_BLOCK;
|
||||
}
|
||||
|
||||
public Material getTransitionMaterial(Biome biome) {
|
||||
String biomeName = biome.toString().toLowerCase();
|
||||
if (biomeName.contains("beach") || biomeName.contains("desert")) {
|
||||
return Material.SANDSTONE;
|
||||
} else if (biomeName.contains("stone")) {
|
||||
return Material.STONE;
|
||||
} else if (biomeName.contains("ocean")) {
|
||||
return Material.GRAVEL;
|
||||
}
|
||||
return Material.DIRT;
|
||||
}
|
||||
|
||||
public int getTransitionMaterialThickness(int worldX, int worldZ, Biome biome) {
|
||||
return (int) (Math.abs(mapper.getWorldValue(worldX, worldZ)) * 10) + 6;
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package ca.recrown.islandsurvivalcraft.world;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@ -29,11 +30,11 @@ public class IslandWorldMapperTest {
|
||||
answers[x][y] = mapper.getWorldValue(x, y);
|
||||
}
|
||||
}
|
||||
blockValCache.clearCache();
|
||||
}
|
||||
|
||||
|
||||
@AfterEach
|
||||
public void individualCleanup() {
|
||||
random.setSeed(SEED);
|
||||
blockValCache.clearCache();
|
||||
}
|
||||
|
||||
@ -60,4 +61,15 @@ public class IslandWorldMapperTest {
|
||||
assertEquals(answers[x][y], mapper.getWorldValue(x, y), String.format("Occurred at (%d, %d)", x, y));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2048ValuesValidity() {
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
int x = random.nextInt();
|
||||
int y = random.nextInt();
|
||||
assertTrue((mapper.getWorldValue(x, y) <= 1));
|
||||
assertTrue((mapper.getWorldValue(x, y) >= -1));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user