Preliminary new unique biome generator.

This commit is contained in:
Harrison Deng 2020-05-07 11:52:23 -05:00
parent a5cf40913b
commit dd89266b4e
11 changed files with 145 additions and 1390 deletions

View File

@ -0,0 +1,8 @@
package ca.recrown.islandsurvivalcraft.utilities.biomes;
import org.bukkit.block.Biome;
public interface BiomeInfo {
public Biome getMainBiome();
public Biome getTransitionBiome();
}

View File

@ -4,7 +4,7 @@ import org.bukkit.block.Biome;
import ca.recrown.islandsurvivalcraft.utilities.weighting.Weightable; import ca.recrown.islandsurvivalcraft.utilities.weighting.Weightable;
public class LandBiomeInfo implements Weightable { public class LandBiomeInfo implements Weightable, BiomeInfo {
private final Biome biome; private final Biome biome;
private final float weight; private final float weight;
private final Biome hillBiome; private final Biome hillBiome;
@ -47,14 +47,15 @@ public class LandBiomeInfo implements Weightable {
} }
/** /**
* @return the hill biome. May be null. * @return the hill biome. Will return the normal biome if there is no special hill biome for this biome.
*/ */
public Biome getHillBiome() { public Biome getHillBiome() {
if (hillBiome == null) return getBiome();
return hillBiome; return hillBiome;
} }
/** /**
* @return the shore biome. * @return the shore biome. Should not have shore if null is returned.
*/ */
public Biome getPreferredShore() { public Biome getPreferredShore() {
return preferredShore; return preferredShore;
@ -66,4 +67,9 @@ public class LandBiomeInfo implements Weightable {
public Biome getTransitionBiome() { public Biome getTransitionBiome() {
return transitionBiome; return transitionBiome;
} }
@Override
public Biome getMainBiome() {
return biome;
}
} }

View File

@ -2,7 +2,7 @@ package ca.recrown.islandsurvivalcraft.utilities.biomes;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
public class OceanBiomeInfo { public class OceanBiomeInfo implements BiomeInfo {
private final Biome ocean; private final Biome ocean;
private final Biome transitionOcean; private final Biome transitionOcean;
private final Biome deepAlternative; private final Biome deepAlternative;
@ -22,9 +22,10 @@ public class OceanBiomeInfo {
} }
/** /**
* @return the transition ocean. May be null. * @return the transition ocean. Will return the normal biome if no transitional biome is designated.
*/ */
public Biome getTransitionOcean() { public Biome getTransitionOcean() {
if (transitionOcean == null) return getOcean();
return transitionOcean; return transitionOcean;
} }
@ -34,4 +35,14 @@ public class OceanBiomeInfo {
public Biome getDeepAlternative() { public Biome getDeepAlternative() {
return deepAlternative; return deepAlternative;
} }
@Override
public Biome getMainBiome() {
return getOcean();
}
@Override
public Biome getTransitionBiome() {
return getTransitionBiome();
}
} }

View File

@ -169,21 +169,20 @@ public class IslandWorldMap {
if (!isIsland(worldX, worldZ)) throw new IllegalArgumentException("The given coordinates are not part is an island."); if (!isIsland(worldX, worldZ)) throw new IllegalArgumentException("The given coordinates are not part is an island.");
dfs.setStartPosition(worldX, worldZ); dfs.setStartPosition(worldX, worldZ);
final Point2 goal = new Point2(0, 0); final Point2 goal = new Point2(0, 0);
dfs.setEndPosition(goal);
Reference<Point2> closest = new Reference<>(); Reference<Point2> closest = new Reference<>();
dfs.findTarget(new CoordinateTargetValidatable(){ dfs.findTarget(new CoordinateTargetValidatable(){
Double closestDistance = null; Double closestDistance = null;
@Override @Override
public boolean isCoordinateTarget(int x, int y) { public boolean isCoordinateTarget(int x, int y) {
if (coordinateTargetValidatable != null && coordinateTargetValidatable.isCoordinateTarget(x, y)) {
closest.value = null;
return true;
}
Point2 current = new Point2(x, y); Point2 current = new Point2(x, y);
double currentDistance = current.distance(goal); double currentDistance = current.distance(goal);
if (closestDistance == null || (currentDistance) < closestDistance) { if (closestDistance == null || (currentDistance) < closestDistance) {
closestDistance = currentDistance; closestDistance = currentDistance;
closest.value = current; closest.value = current;
if (coordinateTargetValidatable != null) {
return coordinateTargetValidatable.isCoordinateTarget(x, y);
}
} }
return false; return false;
} }

View File

@ -1,8 +1,6 @@
package ca.recrown.islandsurvivalcraft.world.generation.biomes; package ca.recrown.islandsurvivalcraft.world.generation.biomes;
import org.bukkit.World; import ca.recrown.islandsurvivalcraft.utilities.biomes.BiomeInfo;
import org.bukkit.block.Biome;
import ca.recrown.islandsurvivalcraft.utilities.caching.Cache; import ca.recrown.islandsurvivalcraft.utilities.caching.Cache;
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2; import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
import ca.recrown.islandsurvivalcraft.world.BiomeMap; import ca.recrown.islandsurvivalcraft.world.BiomeMap;
@ -17,16 +15,15 @@ public interface BiomeGenerator {
* It doesn't need to be populated on the first call as this method will be called once for every column in the chunk. * It doesn't need to be populated on the first call as this method will be called once for every column in the chunk.
* However, if some biomes can be set without repetative calls, doing so will prevent this method from being called for those locals. * However, if some biomes can be set without repetative calls, doing so will prevent this method from being called for those locals.
* *
* @param chunkBiomeSets The array of biomes that are to be saturated. First two arrays represent the x and y values, and the third represents the biome set. * @param localBiomeInfo The array of biomes that are to be saturated. First two arrays represent the x and y values, and the third represents the biome set.
* @param world The current world.
* @param chunkX The X coordinate of the chunk. * @param chunkX The X coordinate of the chunk.
* @param chunkZ The Z coordinate of the chunk. * @param chunkZ The Z coordinate of the chunk.
* @param localX The X coordinate of the column within the chunk. * @param localX The X coordinate of the column within the chunk.
* @param localZ The Z coordinate of the column within the chunk. * @param localZ The Z coordinate of the column within the chunk.
* @param mapper The island mapper to be used. * @param islandMap The island mapper to be used.
* @param tempGenerator The temperature generator to be used. * @param tempGenerator The temperature generator to be used.
* @param biomeCache Cache for biomes. * @param biomeCache Cache for biomes.
* @param chunkGenCache Cache for whether or not the chunk is generated. * @param chunkGenCache Cache for whether or not the chunk is generated.
*/ */
public void generateBiomeColumn(Biome[][][] chunkBiomeSets, World world, int chunkX, int chunkZ, int localX, int localZ, IslandWorldMap mapper, BiomeMap biomeSelector, TemperatureMap tempGenerator, Cache<Point2, Biome[]> biomeCache); public void generateBiomeColumn(BiomeInfo[][] localBiomeInfo, int chunkX, int chunkZ, int localX, int localZ, IslandWorldMap islandMap, BiomeMap biomeSelector, TemperatureMap tempGenerator, Cache<Point2, BiomeInfo> biomeCache);
} }

View File

@ -1,157 +1,64 @@
package ca.recrown.islandsurvivalcraft.world.generation.biomes; package ca.recrown.islandsurvivalcraft.world.generation.biomes;
import java.util.Arrays;
import org.bukkit.World;
import org.bukkit.block.Biome;
import ca.recrown.islandsurvivalcraft.utilities.GeneralUtilities; import ca.recrown.islandsurvivalcraft.utilities.GeneralUtilities;
import ca.recrown.islandsurvivalcraft.utilities.biomes.LandBiomeInfo; import ca.recrown.islandsurvivalcraft.utilities.biomes.BiomeInfo;
import ca.recrown.islandsurvivalcraft.utilities.biomes.OceanBiomeInfo;
import ca.recrown.islandsurvivalcraft.utilities.caching.Cache; import ca.recrown.islandsurvivalcraft.utilities.caching.Cache;
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2; import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Reference;
import ca.recrown.islandsurvivalcraft.utilities.floodfill.Floodable; import ca.recrown.islandsurvivalcraft.utilities.floodfill.Floodable;
import ca.recrown.islandsurvivalcraft.utilities.floodfill.Flooder; import ca.recrown.islandsurvivalcraft.utilities.floodfill.Flooder;
import ca.recrown.islandsurvivalcraft.utilities.pathfinding.CoordinateTargetValidatable; import ca.recrown.islandsurvivalcraft.utilities.pathfinding.CoordinateTargetValidatable;
import ca.recrown.islandsurvivalcraft.utilities.pathfinding.CoordinateValidatable;
import ca.recrown.islandsurvivalcraft.world.BiomeMap; import ca.recrown.islandsurvivalcraft.world.BiomeMap;
import ca.recrown.islandsurvivalcraft.world.IslandWorldMap; import ca.recrown.islandsurvivalcraft.world.IslandWorldMap;
import ca.recrown.islandsurvivalcraft.world.TemperatureMap; import ca.recrown.islandsurvivalcraft.world.TemperatureMap;
public class UniqueBiomeGenerator implements BiomeGenerator { public class UniqueBiomeGenerator implements BiomeGenerator {
private final double DEEP_OCEAN_PORTION = 0.5d;
private final double HILL_PORTION = 0.4;
@Override @Override
public void generateBiomeColumn(Biome[][][] chunkBiomeSets, World world, int chunkX, int chunkZ, int localX, int localZ, IslandWorldMap islandMap, BiomeMap biomeSelector, TemperatureMap tempGen, Cache<Point2, Biome[]> biomeCache) { public void generateBiomeColumn(BiomeInfo[][] localBiomeInfo, int chunkX, int chunkZ, int localX, int localZ,
int worldX = 16 * chunkX + localX; IslandWorldMap islandMap, BiomeMap biomeSelector, TemperatureMap tempGenerator,
int worldZ = 16 * chunkZ + localZ; Cache<Point2, BiomeInfo> biomeCache) {
int worldX = 16 * chunkX + localX;
int worldZ = 16 * chunkZ + localZ;
Point2 worldCoords = new Point2(worldX, worldZ);
//Check if we can just give it something in cache. localBiomeInfo[localX][localZ] = biomeCache.get(worldCoords);
Biome[] biomeSet = attemptGetBiomeSet(world, worldX, worldZ, biomeCache, null); if (localBiomeInfo[localX][localZ] != null) return;
if (biomeSet != null) {
chunkBiomeSets[localX][localZ] = biomeSet;
return;
}
//Fine, check if it's ocean. float temperature = tempGenerator.getTemperature(worldX, worldZ);
if (!islandMap.isIsland(worldX, worldZ)) { if (!islandMap.isIsland(worldX, worldZ)) {
biomeSet = chunkBiomeSets[localX][localZ]; BiomeInfo oceanInfo = biomeSelector.getOceanBiome(temperature);
OceanBiomeInfo ocean = biomeSelector.getOceanBiome(tempGen.getTemperature(worldX, worldZ)); localBiomeInfo[localX][localZ] = oceanInfo;
biomeSet[0] = islandMap.getWorldValue(worldX, worldZ) >= -DEEP_OCEAN_PORTION ? ocean.getOcean() : ocean.getDeepAlternative(); biomeCache.set(worldCoords, oceanInfo);
setCacheBiome(worldX, worldZ, biomeSet, chunkBiomeSets, biomeCache); return;
return;
}
//Shoot, looks like it's actually an island.
IslandInfo islandInfo = new IslandInfo(islandMap, world, biomeCache);
Point2 islandOrigin = islandMap.findIslandOrigin(worldX, worldZ, islandInfo);
if (!islandInfo.isComplete()) {
float temp = tempGen.getTemperature(islandOrigin.x, islandOrigin.y);
LandBiomeInfo biomeInfo = biomeSelector.getLandBiomeInfo(temp, islandOrigin.x, islandOrigin.y);
islandInfo.biomeset = new Biome[5];
islandInfo.biomeset[1] = biomeInfo.getBiome();
islandInfo.biomeset[2] = biomeInfo.getPreferredShore() == null ? Biome.BEACH : biomeInfo.getPreferredShore();
islandInfo.biomeset[4] = biomeInfo.getHillBiome() == null ? islandInfo.biomeset[1] : biomeInfo.getHillBiome();
islandInfo.biomeset[3] = islandInfo.biomeset[2];
}
PropagatorInfo propInfo = new PropagatorInfo(islandInfo.biomeset, chunkBiomeSets, new Point2(chunkX, chunkZ), islandMap, biomeCache);
Flooder flooder = new Flooder(new Point2(worldX, worldZ), propInfo);
flooder.start();
}
private Biome[] attemptGetBiomeSet(World world, int worldX, int worldZ, Cache<Point2, Biome[]> biomeCache, Biome[][][] chunkBiomeSets) {
Point2 worldCoords = new Point2(worldX, worldZ);
Biome[] res = null;
if (chunkBiomeSets != null) {
Point2 localCoords = GeneralUtilities.worldToLocalChunkCoordinates(worldCoords);
return chunkBiomeSets[localCoords.x][localCoords.y];
}
res = biomeCache.get(worldCoords);
return res;
}
private void setCacheBiome(int worldX, int worldZ, Biome[] biomeSet, Biome[][][] localBiomes, Cache<Point2, Biome[]> biomeCache) {
Point2 worldCoords = new Point2(worldX, worldZ);
if (localBiomes != null) {
Point2 localCoords = GeneralUtilities.worldToLocalChunkCoordinates(worldCoords);
localBiomes[localCoords.x][localCoords.y] = biomeSet;
}
biomeCache.set(worldCoords, biomeSet);
}
private class IslandInfo implements CoordinateTargetValidatable, CoordinateValidatable {
public final IslandWorldMap mapper;
public final World world;
private final Cache<Point2, Biome[]> biomeCache;
private Biome[] biomeset;
public IslandInfo(IslandWorldMap mapper, World world, Cache<Point2, Biome[]> biomeCache) {
this.mapper = mapper;
this.world = world;
this.biomeCache = biomeCache;
}
@Override
public boolean isCoordinateTarget(int x, int y) {
biomeset = attemptGetBiomeSet(world, x, y, biomeCache, null);
if (biomeset != null) {
if (!isComplete()) throw new IllegalStateException("There was an incomplete biome set.");
return true;
}
return false;
}
@Override
public boolean validate(int x, int y) {
return mapper.isIsland(x, y);
}
public boolean isComplete() {
return !(biomeset == null || biomeset[1] == null || biomeset[2] == null || biomeset[3] == null || biomeset[4] == null);
}
}
private class PropagatorInfo implements Floodable {
private final Biome[] biomeset;
private final Biome[][][] biomes;
private final Point2 chunkCoords;
private final IslandWorldMap mapper;
private final Cache<Point2, Biome[]> biomeCache;
public PropagatorInfo(Biome[] biomeset, Biome[][][] biomes, Point2 chunkCoords, IslandWorldMap mapper, Cache<Point2, Biome[]> biomeCache) {
this.biomeset = biomeset;
this.biomes = biomes;
this.chunkCoords = chunkCoords;
this.mapper = mapper;
this.biomeCache = biomeCache;
}
@Override
public boolean flood(Point2 point) {
Point2 chunkCoords = GeneralUtilities.worldToChunkCoordinates(point);
if (!this.chunkCoords.fastEquals(chunkCoords) || !mapper.isIsland(point.x, point.y)) return false;
int x = point.x;
int y = point.y;
Biome[] biomeSet = Arrays.copyOf(biomeset, biomeset.length);
if (mapper.isLand(x, y)) {
if (mapper.isShore(x, y)) {
biomeSet[0] = biomeset[2];
} else if (mapper.getWorldValue(x, y) >= HILL_PORTION) {
biomeSet[0] = biomeset[4];
} else {
biomeSet[0] = biomeset[1];
} }
} else {
biomeSet[0] = biomeset[3];
}
setCacheBiome(x, y, biomeSet, biomes, biomeCache);
return true;
}
Reference<BiomeInfo> currentIslandInfo = new Reference<>();
Point2 islandOrigin = islandMap.findIslandOrigin(worldX, worldZ, new CoordinateTargetValidatable() {
@Override
public boolean isCoordinateTarget(int x, int y) {
Point2 coord = new Point2(x, y);
BiomeInfo info = biomeCache.get(coord);
if (info == null) return false;
currentIslandInfo.value = info;
return true;
}
});
if (currentIslandInfo.value == null) {
currentIslandInfo.value = biomeSelector.getLandBiomeInfo(temperature, islandOrigin.x, islandOrigin.y);
}
Flooder flooder = new Flooder(worldCoords, new Floodable() {
@Override
public boolean flood(Point2 point) {
Point2 chunkCoords = GeneralUtilities.worldToChunkCoordinates(point);
if (chunkCoords.x != chunkX || chunkCoords.y != chunkZ || !islandMap.isIsland(point.x, point.y)) return false;
biomeCache.set(point, currentIslandInfo.value);
Point2 localCoords = GeneralUtilities.worldToLocalChunkCoordinates(point);
localBiomeInfo[localCoords.x][localCoords.y] = currentIslandInfo.value;
return true;
}
});
flooder.start();
} }
} }

View File

@ -14,6 +14,9 @@ import org.bukkit.event.Listener;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import ca.recrown.islandsurvivalcraft.utilities.GeneralUtilities; import ca.recrown.islandsurvivalcraft.utilities.GeneralUtilities;
import ca.recrown.islandsurvivalcraft.utilities.biomes.BiomeInfo;
import ca.recrown.islandsurvivalcraft.utilities.biomes.LandBiomeInfo;
import ca.recrown.islandsurvivalcraft.utilities.biomes.OceanBiomeInfo;
import ca.recrown.islandsurvivalcraft.utilities.caching.Cache; import ca.recrown.islandsurvivalcraft.utilities.caching.Cache;
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2; import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
import ca.recrown.islandsurvivalcraft.world.BiomeMap; import ca.recrown.islandsurvivalcraft.world.BiomeMap;
@ -27,6 +30,9 @@ import ca.recrown.islandsurvivalcraft.world.generation.shaders.WorldHeightShader
import ca.recrown.islandsurvivalcraft.world.generation.shaders.WorldLayerShader; import ca.recrown.islandsurvivalcraft.world.generation.shaders.WorldLayerShader;
public class IslandWorldChunkGenerator extends ChunkGenerator implements Listener { public class IslandWorldChunkGenerator extends ChunkGenerator implements Listener {
private final float TRANSITION_DEPTH_PORTION = 0.1f;
private final float DEEP_OCEAN_PORTION = 0.5f;
private final float HILL_BIOME_PORTION = 0.4f;
private final int BEDROCK_HEIGHT = 5; private final int BEDROCK_HEIGHT = 5;
private volatile GeneratorModes generatorType; private volatile GeneratorModes generatorType;
private volatile boolean initialized = false; private volatile boolean initialized = false;
@ -37,7 +43,7 @@ public class IslandWorldChunkGenerator extends ChunkGenerator implements Listene
private volatile BiomeGenerator biomeGenerator; private volatile BiomeGenerator biomeGenerator;
private volatile WorldHeightShader heightShader; private volatile WorldHeightShader heightShader;
private volatile WorldLayerShader layerShader; private volatile WorldLayerShader layerShader;
private volatile Cache<Point2, Biome[]> biomeCache = new Cache<>(131072); private volatile Cache<Point2, BiomeInfo> biomeCache = new Cache<>(131072);
private final ExecutorService exAlpha = GeneralUtilities.ISC_EXECUTOR_ALPHA; private final ExecutorService exAlpha = GeneralUtilities.ISC_EXECUTOR_ALPHA;
public void initialize() { public void initialize() {
@ -86,40 +92,72 @@ public class IslandWorldChunkGenerator extends ChunkGenerator implements Listene
ChunkData chunkData = createChunkData(world); ChunkData chunkData = createChunkData(world);
Biome[][][] biomeArraySet = new Biome[GeneralUtilities.CHUNK_SIZE][GeneralUtilities.CHUNK_SIZE][5]; BiomeInfo[][] localBiomeInfos = new BiomeInfo[GeneralUtilities.CHUNK_SIZE][GeneralUtilities.CHUNK_SIZE];
for (int x = 0; x < GeneralUtilities.CHUNK_SIZE; x++) { for (int x = 0; x < GeneralUtilities.CHUNK_SIZE; x++) {
for (int z = 0; z < GeneralUtilities.CHUNK_SIZE; z++) { for (int z = 0; z < GeneralUtilities.CHUNK_SIZE; z++) {
final int localX = x; final int localX = x;
final int localZ = z; final int localZ = z;
final int worldX = GeneralUtilities.CHUNK_SIZE * chunkX + localX; final int worldX = GeneralUtilities.CHUNK_SIZE * chunkX + localX;
final int worldZ = GeneralUtilities.CHUNK_SIZE * chunkZ + localZ; final int worldZ = GeneralUtilities.CHUNK_SIZE * chunkZ + localZ;
if (biomeArraySet[localX][localZ][0] == null) { if (localBiomeInfos[localX][localZ] == null) {
biomeGenerator.generateBiomeColumn(biomeArraySet, world, chunkX, chunkZ, localX, localZ, islandMap, biomeMap, biomeGenerator.generateBiomeColumn(localBiomeInfos, chunkX, chunkZ, localX, localZ, islandMap, biomeMap,
temperatureMap, biomeCache); temperatureMap, biomeCache);
} }
if (localBiomeInfos[localX][localZ] == null) throw new IllegalStateException("Biome column produced was null.");
for (int y = 0; y < worldInfo.getWorldHeight(); y++) { Biome currentBiome = null;
biomeGrid.setBiome(localX, y, localZ, biomeArraySet[localX][localZ][0]); if (islandMap.isIsland(worldX, worldZ)) {
LandBiomeInfo biomeInfo = (LandBiomeInfo) localBiomeInfos[localX][localZ];
double islandVal = islandMap.getWorldValue(worldX, worldZ);
if (islandVal >= HILL_BIOME_PORTION) {
currentBiome = biomeInfo.getHillBiome();
for (int y = 0; y < worldInfo.getWorldHeight(); y++) {
biomeGrid.setBiome(localX, y, localZ, biomeInfo.getHillBiome());
}
} else {
currentBiome = biomeInfo.getBiome();
for (int y = 0; y < worldInfo.getWorldHeight(); y++) {
biomeGrid.setBiome(localX, y, localZ, biomeInfo.getBiome());
}
}
} else {
OceanBiomeInfo biomeInfo = (OceanBiomeInfo) localBiomeInfos[localX][localZ];
double oceanVal = islandMap.getWorldValue(worldX, worldZ);
if (oceanVal >= TRANSITION_DEPTH_PORTION) {
currentBiome = biomeInfo.getTransitionOcean();
for (int y = 0; y < worldInfo.getWorldHeight(); y++) {
biomeGrid.setBiome(localX, y, localZ, biomeInfo.getTransitionOcean());
}
} else if (oceanVal >= DEEP_OCEAN_PORTION) {
currentBiome = biomeInfo.getOcean();
for (int y = 0; y < worldInfo.getWorldHeight(); y++) {
biomeGrid.setBiome(localX, y, localZ, biomeInfo.getOcean());
}
} else {
currentBiome = biomeInfo.getDeepAlternative();
for (int y = 0; y < worldInfo.getWorldHeight(); y++) {
biomeGrid.setBiome(localX, y, localZ, biomeInfo.getDeepAlternative());
}
}
} }
Biome[] currentBiomeSet = biomeArraySet[localX][localZ]; BiomeInfo currentBiomeInfo = localBiomeInfos[localX][localZ];
int terrainHeight = heightShader.getTerrainHeight(worldX, worldZ, currentBiomeSet); int terrainHeight = heightShader.getTerrainHeight(worldX, worldZ, currentBiomeInfo);
int currentTerrainHeight = terrainHeight - 1; int currentTerrainHeight = terrainHeight - 1;
int bedrockHeight = random.nextInt(5) + 1; int bedrockHeight = random.nextInt(5) + 1;
if (layerShader.hasSpecialLayers(currentBiomeSet[0])) { if (layerShader.hasSpecialLayers(currentBiome)) {
BlockData currentMaterial = layerShader.getMaterialForHeight(worldInfo.getSeed(), worldX, worldZ, currentTerrainHeight, terrainHeight - 1, currentBiomeSet); BlockData currentMaterial = layerShader.getMaterialForHeight(worldInfo.getSeed(), worldX, worldZ, currentTerrainHeight, terrainHeight - 1, currentBiome);
while (currentMaterial != null) { while (currentMaterial != null) {
chunkData.setBlock(localX, currentTerrainHeight, localZ, currentMaterial); chunkData.setBlock(localX, currentTerrainHeight, localZ, currentMaterial);
currentTerrainHeight--; currentTerrainHeight--;
currentMaterial = layerShader.getMaterialForHeight(worldInfo.getSeed(), worldX, worldZ, currentTerrainHeight, terrainHeight - 1, currentBiomeSet); currentMaterial = layerShader.getMaterialForHeight(worldInfo.getSeed(), worldX, worldZ, currentTerrainHeight, terrainHeight - 1, currentBiome);
} }
} else { } else {
int surfaceThickness = layerShader.getSurfaceThickness(worldX, worldZ, currentBiomeSet[0]); int surfaceThickness = layerShader.getSurfaceThickness(worldX, worldZ, currentBiome);
currentTerrainHeight = currentTerrainHeight - surfaceThickness; currentTerrainHeight = currentTerrainHeight - surfaceThickness;
chunkData.setRegion(localX, currentTerrainHeight, localZ, localX + 1, currentTerrainHeight + surfaceThickness + 1, localZ + 1, layerShader.getSurfaceMaterial(currentBiomeSet[0])); chunkData.setRegion(localX, currentTerrainHeight, localZ, localX + 1, currentTerrainHeight + surfaceThickness + 1, localZ + 1, layerShader.getSurfaceMaterial(currentBiome));
int transitionThickness = layerShader.getTransitionMaterialThickness(worldX, worldZ, currentBiomeSet[0]); int transitionThickness = layerShader.getTransitionMaterialThickness(worldX, worldZ, currentBiome);
currentTerrainHeight = currentTerrainHeight - transitionThickness; currentTerrainHeight = currentTerrainHeight - transitionThickness;
chunkData.setRegion(localX, currentTerrainHeight, localZ, localX + 1, currentTerrainHeight + transitionThickness + 1, localZ + 1, layerShader.getTransitionMaterial(currentBiomeSet[0])); chunkData.setRegion(localX, currentTerrainHeight, localZ, localX + 1, currentTerrainHeight + transitionThickness + 1, localZ + 1, layerShader.getTransitionMaterial(currentBiome));
} }
chunkData.setRegion(localX, bedrockHeight, localZ, localX + 1, currentTerrainHeight + 1, localZ + 1, Material.STONE); chunkData.setRegion(localX, bedrockHeight, localZ, localX + 1, currentTerrainHeight + 1, localZ + 1, Material.STONE);
if (terrainHeight < worldInfo.getSeaLevel()) { if (terrainHeight < worldInfo.getSeaLevel()) {

View File

@ -5,6 +5,7 @@ import java.util.Random;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.util.noise.SimplexOctaveGenerator; import org.bukkit.util.noise.SimplexOctaveGenerator;
import ca.recrown.islandsurvivalcraft.utilities.biomes.BiomeInfo;
import ca.recrown.islandsurvivalcraft.world.IslandWorldMap; import ca.recrown.islandsurvivalcraft.world.IslandWorldMap;
public class WorldHeightShader { public class WorldHeightShader {
@ -23,25 +24,16 @@ public class WorldHeightShader {
this.noiseGenerator.setScale(0.0225d); this.noiseGenerator.setScale(0.0225d);
} }
public int getTerrainHeight(int worldX, int worldZ, Biome[] biomeSet) { public int getTerrainHeight(int worldX, int worldZ, BiomeInfo biomeInfo) {
int height = 0; int height = 0;
String biomeName = biomeSet[0].name().toLowerCase();
if (!mapper.isLand(worldX, worldZ)) { if (!mapper.isLand(worldX, worldZ)) {
height = (int) Math.floor(calculateTerrainFactor(worldX, worldZ, seaLevel * 0.8d, 1.7d, 0.5d, 1d, 0.09d)); height = (int) Math.floor(calculateTerrainFactor(worldX, worldZ, seaLevel * 0.8d, 1.7d, 0.5d, 1d, 0.09d));
} else if (biomeName.contains("beach")) {
if (biomeSet[1].name().toLowerCase().contains("mountains")) {
height = (int) (getIslandBiomeHeight(worldX, worldZ, biomeSet[1], -15d));
} else {
height = (int) (getIslandBiomeHeight(worldX, worldZ, biomeSet[1], 0d));
}
} else if (biomeName.contains("shore")) {
height = (int) (getIslandBiomeHeight(worldX, worldZ, biomeSet[1], -15d));
} else { } else {
height = getIslandBiomeHeight(worldX, worldZ, biomeSet[0], 0d) + 1; height = getIslandBiomeHeight(worldX, worldZ, biomeInfo.getMainBiome(), 0d);
} }
height = Math.max(minimumHeight, height); height = Math.max(minimumHeight, height);
if (height > worldHeight) throw new IllegalStateException(String.format("Resulting height is greater than world height! Current biome set: %s, maximum height: %d, minimum height %d", biomeSet, worldHeight, minimumHeight)); if (height > worldHeight) throw new IllegalStateException(String.format("Resulting height is greater than world height! Current biome info: %s, maximum height: %d, minimum height %d", biomeInfo, worldHeight, minimumHeight));
return height; return height;
} }

View File

@ -54,8 +54,8 @@ public class WorldLayerShader {
* @param biomeSet The biomeset for the current column. * @param biomeSet The biomeset for the current column.
* @return The block data for the material, or null if there are no further special materials. * @return The block data for the material, or null if there are no further special materials.
*/ */
public BlockData getMaterialForHeight(long seed, int worldX, int worldZ, int y, int highestPoint, Biome[] biomeSet) { public BlockData getMaterialForHeight(long seed, int worldX, int worldZ, int y, int highestPoint, Biome currentBiome) {
Biome mainBiome = biomeSet[0]; Biome mainBiome = currentBiome;
String mainBiomeName = mainBiome.toString().toLowerCase(); String mainBiomeName = mainBiome.toString().toLowerCase();
BlockData res = null; BlockData res = null;

View File

@ -10,7 +10,6 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.bukkit.block.Biome;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -20,19 +19,17 @@ import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.TestInstance.Lifecycle;
import ca.recrown.islandsurvivalcraft.utilities.GeneralUtilities; import ca.recrown.islandsurvivalcraft.utilities.GeneralUtilities;
import ca.recrown.islandsurvivalcraft.utilities.biomes.BiomeInfo;
import ca.recrown.islandsurvivalcraft.utilities.caching.Cache; import ca.recrown.islandsurvivalcraft.utilities.caching.Cache;
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2; import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
import ca.recrown.islandsurvivalcraft.world.BiomeMap; import ca.recrown.islandsurvivalcraft.world.BiomeMap;
import ca.recrown.islandsurvivalcraft.world.IslandWorldMap; import ca.recrown.islandsurvivalcraft.world.IslandWorldMap;
import ca.recrown.islandsurvivalcraft.world.TemperatureMap; import ca.recrown.islandsurvivalcraft.world.TemperatureMap;
import ca.recrown.islandsurvivalcraft.world.generation.DummyWorld;
@TestInstance(Lifecycle.PER_CLASS) @TestInstance(Lifecycle.PER_CLASS)
public class UniqueBiomeGeneratorTest { public class UniqueBiomeGeneratorTest {
private final int SEED = 249398015; private final int SEED = 249398015;
private final DummyWorld dummyWorld = new DummyWorld(); private volatile Cache<Point2, BiomeInfo> biomeCache;
private volatile Cache<Point2, Double> blockValueCache;
private volatile Cache<Point2, Biome[]> biomeCache;
private final BiomeMap biomeSelector = new BiomeMap(new Random(SEED)); private final BiomeMap biomeSelector = new BiomeMap(new Random(SEED));
@ -59,11 +56,11 @@ public class UniqueBiomeGeneratorTest {
TemperatureMap temperatureMapGenerator = new TemperatureMap(random); TemperatureMap temperatureMapGenerator = new TemperatureMap(random);
BiomeGenerator biomeGenerator = new UniqueBiomeGenerator(); BiomeGenerator biomeGenerator = new UniqueBiomeGenerator();
Biome[][][] biomes = new Biome[GeneralUtilities.CHUNK_SIZE][GeneralUtilities.CHUNK_SIZE][4]; BiomeInfo[][] biomes = new BiomeInfo[GeneralUtilities.CHUNK_SIZE][GeneralUtilities.CHUNK_SIZE];
for (int localX = 0; localX < GeneralUtilities.CHUNK_SIZE; localX++) { for (int localX = 0; localX < GeneralUtilities.CHUNK_SIZE; localX++) {
for (int localZ = 0; localZ < GeneralUtilities.CHUNK_SIZE; localZ++) { for (int localZ = 0; localZ < GeneralUtilities.CHUNK_SIZE; localZ++) {
if (biomes[localX][localZ] == null) { if (biomes[localX][localZ] == null) {
biomeGenerator.generateBiomeColumn(biomes, dummyWorld, chunkX, chunkZ, localX, localZ, mapper, biomeGenerator.generateBiomeColumn(biomes, chunkX, chunkZ, localX, localZ, mapper,
biomeSelector, temperatureMapGenerator, biomeCache); biomeSelector, temperatureMapGenerator, biomeCache);
} }
if (biomes[localX][localZ] == null) if (biomes[localX][localZ] == null)
@ -79,13 +76,11 @@ public class UniqueBiomeGeneratorTest {
@BeforeEach @BeforeEach
public void individualSetup() { public void individualSetup() {
blockValueCache = new Cache<>(524288);
biomeCache = new Cache<>(524288); biomeCache = new Cache<>(524288);
} }
@AfterEach @AfterEach
public void individualCleanup() { public void individualCleanup() {
blockValueCache.clearCache();
biomeCache.clearCache(); biomeCache.clearCache();
} }
@ -122,7 +117,6 @@ public class UniqueBiomeGeneratorTest {
@Test @Test
@Timeout(value = 1, unit = TimeUnit.MINUTES) @Timeout(value = 1, unit = TimeUnit.MINUTES)
public void testBiomeGenerationMultithread1608ChunksSmallCache() { public void testBiomeGenerationMultithread1608ChunksSmallCache() {
this.blockValueCache = new Cache<>(1024);
this.biomeCache = new Cache<>(1024); this.biomeCache = new Cache<>(1024);
int chunksToDoEach = 268; int chunksToDoEach = 268;
@ -192,7 +186,6 @@ public class UniqueBiomeGeneratorTest {
@Test @Test
@Timeout(value = 1, unit = TimeUnit.MINUTES) @Timeout(value = 1, unit = TimeUnit.MINUTES)
public void testBiomeGenerationMultithread1608ChunksScatteredColumnsSmallCache() { public void testBiomeGenerationMultithread1608ChunksScatteredColumnsSmallCache() {
this.blockValueCache = new Cache<>(1024);
this.biomeCache = new Cache<>(1024); this.biomeCache = new Cache<>(1024);
int chunksToDoEach = 268; int chunksToDoEach = 268;
Runnable g1 = new BiomeGenTask(chunksToDoEach, 0); Runnable g1 = new BiomeGenTask(chunksToDoEach, 0);
@ -254,7 +247,6 @@ public class UniqueBiomeGeneratorTest {
@Test @Test
@Timeout(value = 1, unit = TimeUnit.MINUTES) @Timeout(value = 1, unit = TimeUnit.MINUTES)
public void testBiomeGenerationMultithread6000ChunksScatteredColumnsSmallCache() { public void testBiomeGenerationMultithread6000ChunksScatteredColumnsSmallCache() {
this.blockValueCache = new Cache<>(1024);
this.biomeCache = new Cache<>(1024); this.biomeCache = new Cache<>(1024);
int chunksToDoEach = 1000; int chunksToDoEach = 1000;