Implemented concept of island origin coords.
Useful for hashing an island potentially.
This commit is contained in:
parent
46f393c30e
commit
778b334ceb
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@ -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",
|
||||
|
@ -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;
|
||||
|
@ -0,0 +1,5 @@
|
||||
package ca.recrown.islandsurvivalcraft.utilities.datatypes;
|
||||
|
||||
public class Reference<T> {
|
||||
public T value;
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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];
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user