Chunk generation now generates sea and island.

Adjusted values for the island mapper.
Island world mapper now takes a seed instead of a random object.
Refactoring of method names.

Reduced cache sizes used by terrain generation.

Reworked height shader to not use it's own noise generator.

Updated tests implementing changes respectively.
This commit is contained in:
2020-04-30 21:42:03 -05:00
parent d0701ce63d
commit e4597538b2
5 changed files with 173 additions and 84 deletions

View File

@@ -13,20 +13,20 @@ public class IslandWorldMapper implements CoordinateValidatable {
private final Cache<Point2, Double> blockValueCache;
private final SimplexOctaveGenerator noiseGenerator;
private final int noiseOctaves = 4;
private final float islandBlockGenerationPercent = 16;
private final float exaggerationFactor = 1.2f;
private final float islandValueExaggerationFactor = 0.2f;
private final double noiseFrequency = 1.95D;
private final int noiseOctaves = 8;
private final float islandBlockGenerationPercent = 10;
private final float exaggerationFactor = 1f;
private final float islandExaggeration = 1.6f;
private final double noiseFrequency = 1.8D;
private final double noiseAmplitude = 0.5D;
private final double scale = 0.03D;
private final double scale = 0.01D;
private final float shoreFactor = 0.035f;
private final float shallowPortion = 0.015f;
private final DepthFirstSearch dfs;
public IslandWorldMapper(Random random, Cache<Point2, Double> blockValueCache) {
public IslandWorldMapper(long seed, Cache<Point2, Double> blockValueCache) {
dfs = new DepthFirstSearch(this);
this.noiseGenerator = new SimplexOctaveGenerator(random, noiseOctaves);
this.noiseGenerator = new SimplexOctaveGenerator(new Random(seed), noiseOctaves);
noiseGenerator.setScale(scale);
this.blockValueCache = blockValueCache;
}
@@ -38,7 +38,7 @@ public class IslandWorldMapper implements CoordinateValidatable {
* @return
*/
public boolean isLand(int worldX, int worldZ) {
if (getWorldBlockValue(worldX, worldZ) >= 0) {
if (getWorldValue(worldX, worldZ) >= 0) {
return true;
}
return false;
@@ -68,7 +68,7 @@ public class IslandWorldMapper implements CoordinateValidatable {
*/
public boolean isShore(int worldX, int worldZ) {
if (!isIsland(worldX, worldZ)) return false;
if (isLand(worldX, worldZ) && getWorldBlockValue(worldX, worldZ) <= shoreFactor) {
if (isLand(worldX, worldZ) && getWorldValue(worldX, worldZ) <= shoreFactor) {
return true;
}
return false;
@@ -95,7 +95,7 @@ public class IslandWorldMapper implements CoordinateValidatable {
* @return true if it is considered the shallow portion.
*/
public boolean isShallowPortion(int worldX, int worldZ) {
if (!isLand(worldX, worldZ) && getWorldBlockValue(worldX, worldZ) >= -shallowPortion) {
if (!isLand(worldX, worldZ) && getWorldValue(worldX, worldZ) >= -shallowPortion) {
return true;
}
return false;
@@ -106,24 +106,24 @@ public class IslandWorldMapper implements CoordinateValidatable {
* World block 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 is normalized to [0, 1].
* The value is normalized to [-1, 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 double getWorldBlockValue(int worldX, int worldZ) {
public double getWorldValue(int worldX, int worldZ) {
Point2 p = new Point2(worldX, worldZ);
Double res = blockValueCache.get(p);
if (res == null) {
double portionSea = 1f - (this.islandBlockGenerationPercent / 100f);
double shift = 1f - 2 * portionSea;
double shift = (portionSea - 1d) / 2d;
double rawNoise = noiseGenerator.noise(worldX, worldZ, noiseFrequency, noiseAmplitude, true);
double noise = 0;
if (rawNoise < 0) {
noise = ( - Math.pow(- rawNoise, exaggerationFactor) + shift);
} else {
noise = Math.pow(rawNoise, islandValueExaggerationFactor) + shift;
noise = Math.pow(rawNoise, islandExaggeration) + shift;
}
double maxNeg = -1 + shift;
double maxPos = 1 + shift;

View File

@@ -7,6 +7,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Biome;
@@ -24,9 +25,9 @@ import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
import ca.recrown.islandsurvivalcraft.world.shaders.WorldHeightShader;
public class IslandWorldChunkGenerator extends ChunkGenerator implements Listener {
private final Cache<Point2, Double> blockValueCache = new Cache<>(262144);
private final Cache<Point2, Biome[]> biomeCache = new Cache<>(262144);
private final Cache<Point2, Boolean> chunkExistenceCache = new Cache<>(32768);
private final Cache<Point2, Double> blockValueCache = new Cache<>(131072);
private final Cache<Point2, Biome[]> biomeCache = new Cache<>(131072);
private final Cache<Point2, Boolean> chunkExistenceCache = new Cache<>(131072);
private final BiomeSelector biomeSelector = new BiomeSelector();
private final ExecutorService exAlpha = Utilities.ISC_EXECUTOR_ALPHA;
private final ExecutorService exBeta = Utilities.ISC_EXECUTOR_BETA;
@@ -39,19 +40,21 @@ public class IslandWorldChunkGenerator extends ChunkGenerator implements Listene
@Override
public ChunkData generateChunkData(World world, Random random, int chunkX, int chunkZ, BiomeGrid biomeGrid) {
this.currentWorld = world;
IslandWorldMapper mapper = new IslandWorldMapper(random, blockValueCache);
TemperatureMapGenerator temperatureMapGenerator = new TemperatureMapGenerator(world.getSeed());
WorldHeightShader heightShader = new WorldHeightShader(random, mapper, world.getSeaLevel(), world.getMaxHeight(), 3);
long seed = world.getSeed();
IslandWorldMapper mapper = new IslandWorldMapper(seed, blockValueCache);
TemperatureMapGenerator temperatureMapGenerator = new TemperatureMapGenerator(seed);
WorldHeightShader heightShader = new WorldHeightShader(mapper, world.getSeaLevel(), world.getMaxHeight(), 3);
BiomeGenerator biomeGenerator = new UniBiomeIslandGenerator();
int maxHeight = world.getMaxHeight();
int seaLevel = world.getSeaLevel();
LinkedList<Future<Boolean>> tasks = new LinkedList<>();
ChunkData chunkData = createChunkData(world);
Future<Boolean> preLoader = exAlpha.submit(() -> {
for (int x = Utilities.CHUNK_SIZE - 1; x >= 0; x--) {
for (int z = Utilities.CHUNK_SIZE - 1; z >= 0; z--) {
if (Thread.currentThread().isInterrupted()) return false;
mapper.getWorldBlockValue(Utilities.CHUNK_SIZE * chunkX + x, Utilities.CHUNK_SIZE * chunkZ + z);
mapper.getWorldValue(Utilities.CHUNK_SIZE * chunkX + x, Utilities.CHUNK_SIZE * chunkZ + z);
}
}
return true;
@@ -76,11 +79,15 @@ public class IslandWorldChunkGenerator extends ChunkGenerator implements Listene
final int worldX = Utilities.CHUNK_SIZE * chunkX + localX;
final int worldZ = Utilities.CHUNK_SIZE * chunkZ + localZ;
int height = heightShader.getAltitude(worldX, worldZ, biomes[localX][localZ]);
chunkData.setRegion(localX, 1, localZ, localX + 1, height, localZ + 1, Material.DIAMOND_BLOCK);
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);
if (terrainHeight < seaLevel) {
chunkData.setRegion(localX, terrainHeight, localZ, localX + 1, seaLevel, localZ + 1, Material.WATER);
}
chunkData.setRegion(localX, 0, localZ, localX + 1, bedrockHeight, localZ + 1, Material.BEDROCK);
}
}
chunkData.setRegion(0, 0, 0, 16, 1, 16, Material.BEDROCK);
preLoader.cancel(false);
try {
@@ -93,12 +100,14 @@ public class IslandWorldChunkGenerator extends ChunkGenerator implements Listene
}
return chunkData;
}
@Override
public boolean canSpawn(World world, int x, int z) {
return super.canSpawn(world, x, z);
public Location getFixedSpawnLocation(World world, Random random) {
Location location = new Location(world, 0, 128, 0);
return location;
}
@Override
public boolean isParallelCapable() {
return true;

View File

@@ -1,79 +1,52 @@
package ca.recrown.islandsurvivalcraft.world.shaders;
import java.util.Random;
import org.bukkit.block.Biome;
import org.bukkit.util.noise.SimplexOctaveGenerator;
import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
public class WorldHeightShader {
private final SimplexOctaveGenerator noiseGenerator;
private final IslandWorldMapper islandLocator;
private final int seaLevel;
private final int worldHeight;
private final int minimumHeight;
private final float probabilityOfAdditive = 0.75f;
public WorldHeightShader(Random random, IslandWorldMapper islandLocator, int seaLevel, int worldHeight, int minimumHeight) {
noiseGenerator = new SimplexOctaveGenerator(random, 2);
noiseGenerator.setScale(0.075D);
public WorldHeightShader(IslandWorldMapper islandLocator, int seaLevel, int worldHeight, int minimumHeight) {
this.islandLocator = islandLocator;
this.seaLevel = seaLevel;
this.worldHeight = worldHeight;
this.minimumHeight = minimumHeight;
}
public int getAltitude(int worldX, int worldZ, Biome biome) {
double modifier = getHeightModifier(worldX, worldZ);
int baseValue = calculateTerrainHeight(worldX, worldZ);
public int getTerrainHeight(int worldX, int worldZ, Biome biome) {
int height = 0;
String biomeName = biome.name().toLowerCase();
if (biomeName.contains("hills")) {
height = (int) (modifier * 20D + baseValue);
height = calculateTerrainHeight(worldX, worldZ, 15);
} else if (biomeName.contains("mountains")) {
height = (int) (modifier * 45D + baseValue);
height = calculateTerrainHeight(worldX, worldZ, 30);
} else if (biomeName.contains("plateau")) {
height = (int) (Math.min(60, Math.max(2, modifier * 80D)) + baseValue);
height = Math.min(calculateTerrainHeight(worldX, worldZ, 70), 40);
} else if (biomeName.contains("modified")) {
height = (int) (modifier * 25D + baseValue);
height = calculateTerrainHeight(worldX, worldZ, 45);
} else if (biomeName.contains("shattered")) {
height = (int) (modifier * 35D + baseValue);
height = calculateTerrainHeight(worldX, worldZ, 60);
} else if (biomeName.contains("tall")) {
height = (int) (modifier * 30D + baseValue);
height = calculateTerrainHeight(worldX, worldZ, 45);
} else if (!biomeName.contains("ocean")) {
height = (int) (getNormalizedHeightModifier(worldX, worldZ) * 15D + baseValue);
height = calculateTerrainHeight(worldX, worldZ, 15);
} else {
height = (int) (getNormalizedHeightModifier(worldX, worldZ) * 5D + baseValue);
height = calculateTerrainHeight(worldX, worldZ, seaLevel);
}
if (height > worldHeight) throw new IllegalStateException("Resulting height is greater than world height! Biome this occurred on: " + biomeName);
height = Math.max(minimumHeight, height);
return height;
}
private int calculateTerrainHeight(int worldX, int worldZ) {
double islandValue = islandLocator.getWorldBlockValue(worldX, worldZ) + 1D;
islandValue *= worldHeight/4;
return (int) Math.max(Math.min(seaLevel, islandValue), minimumHeight);
}
private double getHeightModifier(int worldX, int worldZ) {
return (2 * probabilityOfAdditive - noiseGenerator.noise(worldX, worldZ, 0.5D, 0.5D, true));
}
private double maxHeightModifier() {
return 2 * probabilityOfAdditive + 1;
}
private double minHeightModifier() {
return 2 * probabilityOfAdditive - 1;
}
private double getNormalizedHeightModifier(int worldX, int worldZ) {
double heightModifier = getHeightModifier(worldX, worldZ);
if (heightModifier > 0) {
return heightModifier / maxHeightModifier();
}
return - heightModifier / minHeightModifier();
private int calculateTerrainHeight(int worldX, int worldZ, int multiplier) {
int blockHeight = seaLevel;
double islandValue = islandLocator.getWorldValue(worldX, worldZ);
blockHeight += islandValue * multiplier;
return blockHeight;
}
}