Implemented concept of island origin coords.

Useful for hashing an island potentially.
This commit is contained in:
Harrison Deng 2020-05-05 16:49:21 -05:00
parent 46f393c30e
commit 778b334ceb
9 changed files with 120 additions and 67 deletions

7
.vscode/launch.json vendored
View File

@ -4,13 +4,6 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "CodeLens (Launch) - UniBiomeIslandGeneratorTest",
"request": "launch",
"mainClass": "ca.recrown.islandsurvivalcraft.world.generation.UniBiomeIslandGeneratorTest",
"projectName": "IslandSurvivalCraft"
},
{
"type": "java",
"name": "Debug (Launch) - Current File",

View File

@ -25,6 +25,10 @@ public class Point2 {
return point.hashCode() == this.hash && x == point.x && y == point.y;
}
public double distance(Point2 point) {
return Math.hypot(point.x - this.x, point.y - this.y);
}
@Override
public int hashCode() {
return hash;

View File

@ -0,0 +1,5 @@
package ca.recrown.islandsurvivalcraft.utilities.datatypes;
public class Reference<T> {
public T value;
}

View File

@ -6,10 +6,12 @@ import org.bukkit.util.noise.SimplexOctaveGenerator;
import ca.recrown.islandsurvivalcraft.utilities.caching.Cache;
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Reference;
import ca.recrown.islandsurvivalcraft.utilities.pathfinding.CoordinateTargetValidatable;
import ca.recrown.islandsurvivalcraft.utilities.pathfinding.CoordinateValidatable;
import ca.recrown.islandsurvivalcraft.utilities.pathfinding.DepthFirstSearch;
public class IslandWorldMap implements CoordinateValidatable {
public class IslandWorldMap {
private final Cache<Point2, Double> blockValueCache;
private final SimplexOctaveGenerator noiseGenerator;
@ -24,7 +26,13 @@ public class IslandWorldMap implements CoordinateValidatable {
private final DepthFirstSearch dfs;
public IslandWorldMap(Random random) {
dfs = new DepthFirstSearch(this);
dfs = new DepthFirstSearch(new CoordinateValidatable(){
@Override
public boolean validate(int x, int y) {
return isIsland(x, y);
}
});
this.noiseGenerator = new SimplexOctaveGenerator(random, NOISE_OCTAVES);
noiseGenerator.setScale(SCALE);
this.blockValueCache = new Cache<>(131072);
@ -155,8 +163,41 @@ public class IslandWorldMap implements CoordinateValidatable {
return getWorldValue(worldX, worldZ) <= -DEEP_OCEAN_PORTION;
}
@Override
public boolean validate(int x, int y) {
return isIsland(x, y);
/**
* Find the island's origin block. Will call the coordinate target validatable to end early if nessecary.
* @param worldX The x coordinate of the island block in question.
* @param worldZ The y coordinate of the island block in question.
* @param coordinateTargetValidatable The coordinate target validator to use. Optional and is null.
* @return The island origin point.
*/
public Point2 findIslandOrigin(int worldX, int worldZ, CoordinateTargetValidatable coordinateTargetValidatable) {
if (!isIsland(worldX, worldZ)) throw new IllegalArgumentException("The given coordinates are not part is an island.");
dfs.setStartPosition(worldX, worldZ);
final Point2 goal = new Point2(0, 0);
dfs.setEndPosition(goal);
Reference<Point2> closest = new Reference<>();
dfs.findTarget(new CoordinateTargetValidatable(){
Double closestDistance = null;
@Override
public boolean isCoordinateTarget(int x, int y) {
Point2 current = new Point2(x, y);
double currentDistance = current.distance(goal);
if (closestDistance == null || (currentDistance) < closestDistance) {
closestDistance = currentDistance;
closest.value = current;
if (coordinateTargetValidatable != null) {
return coordinateTargetValidatable.isCoordinateTarget(x, y);
}
}
return false;
}
});
return closest.value;
}
public Point2 findIslandOrigin(int worldX, int worldZ) {
return findIslandOrigin(worldX, worldZ, null);
}
}

View File

@ -28,5 +28,5 @@ public interface BiomeGenerator {
* @param biomeCache Cache for biomes.
* @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, Cache<Point2, Boolean> chunkGenCache);
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);
}

View File

@ -9,21 +9,19 @@ import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
import ca.recrown.islandsurvivalcraft.utilities.floodfill.Floodable;
import ca.recrown.islandsurvivalcraft.utilities.floodfill.Flooder;
import ca.recrown.islandsurvivalcraft.utilities.pathfinding.CoordinateTargetValidatable;
import ca.recrown.islandsurvivalcraft.utilities.pathfinding.DepthFirstSearch;
import ca.recrown.islandsurvivalcraft.utilities.pathfinding.CoordinateValidatable;
import ca.recrown.islandsurvivalcraft.world.BiomeMap;
import ca.recrown.islandsurvivalcraft.world.IslandWorldMap;
import ca.recrown.islandsurvivalcraft.world.TemperatureMap;
public class UniqueBiomeGenerator implements BiomeGenerator {
@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, Cache<Point2, Boolean> chunkGenCache) {
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) {
int worldX = 16 * chunkX + localX;
int worldZ = 16 * chunkZ + localZ;
Point2 chunkCoords = GeneralUtilities.worldToChunkCoordinates(new Point2(worldX, worldZ));
chunkGenCache.set(chunkCoords, false);
//Check if we can just give it something in cache.
Biome[] biomeSet = attemptGetBiomeSet(world, worldX, worldZ, biomeCache, chunkGenCache, null);
Biome[] biomeSet = attemptGetBiomeSet(world, worldX, worldZ, biomeCache, null);
if (biomeSet != null) {
chunkBiomeSets[localX][localZ] = biomeSet;
return;
@ -38,16 +36,13 @@ public class UniqueBiomeGenerator implements BiomeGenerator {
}
//Shoot, looks like it's actually an island.
DepthFirstSearch search = new DepthFirstSearch();
search.setValidatable(islandMap);
IslandInfo islandInfo = new IslandInfo(islandMap, world, biomeCache, chunkGenCache);
search.setStartPosition(worldX, worldZ);
if (!search.findTarget(islandInfo)) {
IslandInfo islandInfo = new IslandInfo(islandMap, world, biomeCache);
Point2 islandOrigin = islandMap.findIslandOrigin(worldX, worldZ, islandInfo);
if (!islandInfo.isComplete()) {
float temp = tempGen.getTemperature(worldX, worldZ);
if (islandInfo.main == null) islandInfo.main = biomeSelector.getLandBiome(temp, worldX, worldZ);
if (islandInfo.main == null) islandInfo.main = biomeSelector.getLandBiome(temp, islandOrigin.x, islandOrigin.y);
if (islandInfo.shore == null) islandInfo.shore = biomeSelector.getShoreBiome(islandInfo.main, temp);
if (islandInfo.shallow == null) islandInfo.shallow = biomeSelector.getOceanBiome(temp, islandMap.isDeepOcean(worldX, worldZ), worldX, worldZ);
if (islandInfo.shallow == null) islandInfo.shallow = biomeSelector.getOceanBiome(temp, islandMap.isDeepOcean(islandOrigin.x, islandOrigin.y), islandOrigin.x, islandOrigin.y);
}
PropagatorInfo propInfo = new PropagatorInfo(islandInfo, chunkBiomeSets, new Point2(chunkX, chunkZ), islandMap, biomeCache);
@ -55,7 +50,7 @@ public class UniqueBiomeGenerator implements BiomeGenerator {
flooder.start();
}
private Biome[] attemptGetBiomeSet(World world, int worldX, int worldZ, Cache<Point2, Biome[]> biomeCache, Cache<Point2, Boolean> existenceCache, Biome[][][] chunkBiomeSets) {
private Biome[] attemptGetBiomeSet(World world, int worldX, int worldZ, Cache<Point2, Biome[]> biomeCache, Biome[][][] chunkBiomeSets) {
Point2 worldCoords = new Point2(worldX, worldZ);
Biome[] res = null;
@ -65,20 +60,6 @@ public class UniqueBiomeGenerator implements BiomeGenerator {
}
res = biomeCache.get(worldCoords);
if (res != null) return res;
Point2 chunkCoords = GeneralUtilities.worldToChunkCoordinates(worldCoords);
Boolean chunkExists = existenceCache.get(chunkCoords);
if (chunkExists == null) {
chunkExists = world.isChunkGenerated(worldX, worldZ);
existenceCache.set(chunkCoords, chunkExists);
}
if (chunkExists) {
res = new Biome[4];
res[0] = world.getBiome(worldX, 0, worldZ);
biomeCache.set(worldCoords, res);
}
return res;
}
@ -91,23 +72,21 @@ public class UniqueBiomeGenerator implements BiomeGenerator {
biomeCache.set(worldCoords, biomeSet);
}
private class IslandInfo implements CoordinateTargetValidatable {
private class IslandInfo implements CoordinateTargetValidatable, CoordinateValidatable {
public final IslandWorldMap mapper;
public final World world;
private final Cache<Point2, Biome[]> biomeCache;
private final Cache<Point2, Boolean> chunkGenCache;
public Biome main, shore, shallow;
public IslandInfo(IslandWorldMap mapper, World world, Cache<Point2, Biome[]> biomeCache, Cache<Point2, Boolean> chunkGenCache) {
public IslandInfo(IslandWorldMap mapper, World world, Cache<Point2, Biome[]> biomeCache) {
this.mapper = mapper;
this.world = world;
this.biomeCache = biomeCache;
this.chunkGenCache = chunkGenCache;
}
@Override
public boolean isCoordinateTarget(int x, int y) {
Biome[] biomeSet = attemptGetBiomeSet(world, x, y, biomeCache, chunkGenCache, null);
Biome[] biomeSet = attemptGetBiomeSet(world, x, y, biomeCache, null);
if (biomeSet != null) {
main = biomeSet[1];
shore = biomeSet[2];
@ -122,6 +101,15 @@ public class UniqueBiomeGenerator implements BiomeGenerator {
shallow = biomeSet[0];
}
}
return isComplete();
}
@Override
public boolean validate(int x, int y) {
return mapper.isIsland(x, y);
}
public boolean isComplete() {
return main != null && shore != null && shallow != null;
}
}
@ -146,7 +134,7 @@ public class UniqueBiomeGenerator implements BiomeGenerator {
@Override
public boolean flood(Point2 point) {
Point2 chunkCoords = GeneralUtilities.worldToChunkCoordinates(point);
if (!this.chunkCoords.fastEquals(chunkCoords) || !mapper.validate(point.x, point.y)) return false;
if (!this.chunkCoords.fastEquals(chunkCoords) || !mapper.isIsland(point.x, point.y)) return false;
int x = point.x;
int y = point.y;
Biome[] biomeSet = new Biome[4];

View File

@ -40,7 +40,6 @@ public class IslandWorldChunkGenerator extends ChunkGenerator implements Listene
private volatile WorldHeightShader heightShader;
private volatile WorldLayerShader layerShader;
private volatile Cache<Point2, Biome[]> biomeCache = new Cache<>(131072);
private volatile Cache<Point2, Boolean> chunkLoadedCache = new Cache<>(4096);
private volatile ExecutorService exAlpha = GeneralUtilities.ISC_EXECUTOR_ALPHA;
private volatile ExecutorService exBeta = GeneralUtilities.ISC_EXECUTOR_BETA;
@ -100,7 +99,7 @@ public class IslandWorldChunkGenerator extends ChunkGenerator implements Listene
final int worldZ = GeneralUtilities.CHUNK_SIZE * chunkZ + localZ;
if (biomeArraySet[localX][localZ][0] == null) {
biomeGenerator.generateBiomeColumn(biomeArraySet, world, chunkX, chunkZ, localX, localZ, islandMap, biomeMap,
temperatureMap, biomeCache, chunkLoadedCache);
temperatureMap, biomeCache);
}
if (biomeArraySet[localX][localZ][0] == null) throw new IllegalStateException("Biome was null.");
tasks.add(exBeta.submit(() -> {
@ -146,7 +145,6 @@ public class IslandWorldChunkGenerator extends ChunkGenerator implements Listene
e.printStackTrace();
throw new IllegalStateException(e.getCause().getMessage());
}
chunkLoadedCache.set(new Point2(chunkX, chunkZ), true);
return chunkData;
}

View File

@ -6,17 +6,19 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Random;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
@TestInstance(Lifecycle.PER_CLASS)
public class IslandWorldMapTest {
public final int SEED = 102385923;
public final int GRIDSIZE = 2048;
public Random random = new Random(SEED);
public IslandWorldMap mapper = new IslandWorldMap(random);
public final double[][] answers = new double[2048][2048];
public final double[][] answers = new double[GRIDSIZE][GRIDSIZE];
@BeforeAll
public void setUp() {
@ -27,12 +29,6 @@ public class IslandWorldMapTest {
}
}
@BeforeEach
public void individualSetUp() {
random = new Random(SEED);
mapper = new IslandWorldMap(random);
}
@Test
public void testBlockValueConsistency() {
for (int x = 0; x < answers.length; x++) {
@ -44,7 +40,7 @@ public class IslandWorldMapTest {
@Test
public void testBlockValueConsistencyRandom() {
for (int amount = 0; amount < 1024; amount++) {
for (int amount = 0; amount < GRIDSIZE; amount++) {
int x = random.nextInt(answers.length);
int y = random.nextInt(answers[x].length);
assertEquals(answers[x][y], mapper.getWorldValue(x, y), String.format("Occurred at (%d, %d)", x, y));
@ -52,13 +48,47 @@ public class IslandWorldMapTest {
}
@Test
public void test2048ValuesValidity() {
public void testValuesValidity() {
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));
}
}
@Test
public void testIslandOriginConsistency() {
Point2 origin = null;
for (int i = 0; i < GRIDSIZE; i++) {
if (origin != null) {
if (mapper.isSameIsland(origin.x, origin.y, i, 0)) {
assertEquals(origin, mapper.findIslandOrigin(i, 0), String.format("Looking at (%d, 0)", i));
} else {
origin = null;
}
} else if (mapper.isIsland(i, 0)) {
origin = mapper.findIslandOrigin(i, 0);
}
}
}
@Test
public void testIslandOriginConsistency2D() {
int gridSize = 32;
Point2 origin = null;
for (int y = 0; y < gridSize; y++) {
for (int x = 0; x < gridSize; x++) {
if (origin != null) {
if (mapper.isSameIsland(origin.x, origin.y, x, y)) {
assertEquals(origin, mapper.findIslandOrigin(x, y), String.format("Looking at (%d, %d)", x, y));
} else {
origin = null;
}
} else if (mapper.isIsland(x, y)) {
origin = mapper.findIslandOrigin(x, y);
}
}
}
}
}

View File

@ -33,7 +33,6 @@ public class UniqueBiomeGeneratorTest {
private final DummyWorld dummyWorld = new DummyWorld();
private volatile Cache<Point2, Double> blockValueCache;
private volatile Cache<Point2, Biome[]> biomeCache;
private volatile Cache<Point2, Boolean> chunkExistenceCache;
private final BiomeMap biomeSelector = new BiomeMap(new Random(SEED));
@ -65,7 +64,7 @@ public class UniqueBiomeGeneratorTest {
for (int localZ = 0; localZ < GeneralUtilities.CHUNK_SIZE; localZ++) {
if (biomes[localX][localZ] == null) {
biomeGenerator.generateBiomeColumn(biomes, dummyWorld, chunkX, chunkZ, localX, localZ, mapper,
biomeSelector, temperatureMapGenerator, biomeCache, chunkExistenceCache);
biomeSelector, temperatureMapGenerator, biomeCache);
}
if (biomes[localX][localZ] == null)
throw new IllegalStateException("Biome was null.");
@ -82,14 +81,12 @@ public class UniqueBiomeGeneratorTest {
public void individualSetup() {
blockValueCache = new Cache<>(524288);
biomeCache = new Cache<>(524288);
chunkExistenceCache = new Cache<>(16384);
}
@AfterEach
public void individualCleanup() {
blockValueCache.clearCache();
biomeCache.clearCache();
chunkExistenceCache.clearCache();
}
@Test
@ -127,7 +124,6 @@ public class UniqueBiomeGeneratorTest {
public void testBiomeGenerationMultithread1608ChunksSmallCache() {
this.blockValueCache = new Cache<>(1024);
this.biomeCache = new Cache<>(1024);
this.chunkExistenceCache = new Cache<>(1024);
int chunksToDoEach = 268;
Runnable g1 = new BiomeGenTask(chunksToDoEach, 0);
@ -198,7 +194,6 @@ public class UniqueBiomeGeneratorTest {
public void testBiomeGenerationMultithread1608ChunksScatteredColumnsSmallCache() {
this.blockValueCache = new Cache<>(1024);
this.biomeCache = new Cache<>(1024);
this.chunkExistenceCache = new Cache<>(1024);
int chunksToDoEach = 268;
Runnable g1 = new BiomeGenTask(chunksToDoEach, 0);
Runnable g2 = new BiomeGenTask(chunksToDoEach, 2);
@ -261,7 +256,6 @@ public class UniqueBiomeGeneratorTest {
public void testBiomeGenerationMultithread6000ChunksScatteredColumnsSmallCache() {
this.blockValueCache = new Cache<>(1024);
this.biomeCache = new Cache<>(1024);
this.chunkExistenceCache = new Cache<>(1024);
int chunksToDoEach = 1000;
Runnable g1 = new BiomeGenTask(chunksToDoEach, 0);