From 211d0fc751426efdf9d74968b18b07054bcbb735 Mon Sep 17 00:00:00 2001 From: paulevsGitch Date: Wed, 1 Dec 2021 14:28:27 +0300 Subject: [PATCH] Hexagonal biome generator --- .../world/generator/BCLibEndBiomeSource.java | 9 +- .../generator/BCLibNetherBiomeSource.java | 5 +- .../generator/map/hex/HexBiomeChunk.java | 130 ++++++++++ .../world/generator/map/hex/HexBiomeMap.java | 156 ++++++++++++ .../square/SquareBiomeChunk.java} | 110 ++++----- .../square/SquareBiomeMap.java} | 228 +++++++++--------- 6 files changed, 465 insertions(+), 173 deletions(-) create mode 100644 src/main/java/ru/bclib/world/generator/map/hex/HexBiomeChunk.java create mode 100644 src/main/java/ru/bclib/world/generator/map/hex/HexBiomeMap.java rename src/main/java/ru/bclib/world/generator/{BiomeChunk.java => map/square/SquareBiomeChunk.java} (86%) rename src/main/java/ru/bclib/world/generator/{BiomeMap.java => map/square/SquareBiomeMap.java} (80%) diff --git a/src/main/java/ru/bclib/world/generator/BCLibEndBiomeSource.java b/src/main/java/ru/bclib/world/generator/BCLibEndBiomeSource.java index 5debd380..d100eb3a 100644 --- a/src/main/java/ru/bclib/world/generator/BCLibEndBiomeSource.java +++ b/src/main/java/ru/bclib/world/generator/BCLibEndBiomeSource.java @@ -20,6 +20,7 @@ import ru.bclib.config.ConfigKeeper.StringArrayEntry; import ru.bclib.config.Configs; import ru.bclib.noise.OpenSimplexNoise; import ru.bclib.world.biomes.BCLBiome; +import ru.bclib.world.generator.map.hex.HexBiomeMap; import java.awt.Point; import java.util.List; @@ -39,8 +40,8 @@ public class BCLibEndBiomeSource extends BiomeSource { private final SimplexNoise noise; private final Biome centerBiome; private final Biome barrens; - private BiomeMap mapLand; - private BiomeMap mapVoid; + private HexBiomeMap mapLand; + private HexBiomeMap mapVoid; private final long seed; private final Point pos; @@ -74,8 +75,8 @@ public class BCLibEndBiomeSource extends BiomeSource { BiomeAPI.END_LAND_BIOME_PICKER.rebuild(); BiomeAPI.END_VOID_BIOME_PICKER.rebuild(); - this.mapLand = new BiomeMap(seed, GeneratorOptions.getBiomeSizeEndLand(), BiomeAPI.END_LAND_BIOME_PICKER); - this.mapVoid = new BiomeMap(seed, GeneratorOptions.getBiomeSizeEndVoid(), BiomeAPI.END_VOID_BIOME_PICKER); + this.mapLand = new HexBiomeMap(seed, GeneratorOptions.getBiomeSizeEndLand(), BiomeAPI.END_LAND_BIOME_PICKER); + this.mapVoid = new HexBiomeMap(seed, GeneratorOptions.getBiomeSizeEndVoid(), BiomeAPI.END_VOID_BIOME_PICKER); this.centerBiome = biomeRegistry.getOrThrow(Biomes.THE_END); this.barrens = biomeRegistry.getOrThrow(Biomes.END_BARRENS); this.biomeRegistry = biomeRegistry; diff --git a/src/main/java/ru/bclib/world/generator/BCLibNetherBiomeSource.java b/src/main/java/ru/bclib/world/generator/BCLibNetherBiomeSource.java index 8e2dff94..5b8361f9 100644 --- a/src/main/java/ru/bclib/world/generator/BCLibNetherBiomeSource.java +++ b/src/main/java/ru/bclib/world/generator/BCLibNetherBiomeSource.java @@ -14,6 +14,7 @@ import ru.bclib.api.biomes.BiomeAPI; import ru.bclib.config.ConfigKeeper.StringArrayEntry; import ru.bclib.config.Configs; import ru.bclib.world.biomes.BCLBiome; +import ru.bclib.world.generator.map.hex.HexBiomeMap; import java.util.LinkedList; import java.util.List; @@ -28,7 +29,7 @@ public class BCLibNetherBiomeSource extends BiomeSource { })).apply(instance, instance.stable(BCLibNetherBiomeSource::new)); }); private final Registry biomeRegistry; - private BiomeMap biomeMap; + private HexBiomeMap biomeMap; private final long seed; @Deprecated(forRemoval = true) @@ -59,7 +60,7 @@ public class BCLibNetherBiomeSource extends BiomeSource { BiomeAPI.NETHER_BIOME_PICKER.getBiomes().forEach(biome -> biome.updateActualBiomes(biomeRegistry)); BiomeAPI.NETHER_BIOME_PICKER.rebuild(); - this.biomeMap = new BiomeMap(seed, GeneratorOptions.getBiomeSizeNether(), BiomeAPI.NETHER_BIOME_PICKER); + this.biomeMap = new HexBiomeMap(seed, GeneratorOptions.getBiomeSizeNether(), BiomeAPI.NETHER_BIOME_PICKER); this.biomeRegistry = biomeRegistry; this.seed = seed; diff --git a/src/main/java/ru/bclib/world/generator/map/hex/HexBiomeChunk.java b/src/main/java/ru/bclib/world/generator/map/hex/HexBiomeChunk.java new file mode 100644 index 00000000..57d46f79 --- /dev/null +++ b/src/main/java/ru/bclib/world/generator/map/hex/HexBiomeChunk.java @@ -0,0 +1,130 @@ +package ru.bclib.world.generator.map.hex; + +import ru.bclib.world.biomes.BCLBiome; +import ru.bclib.world.generator.BiomePicker; + +import java.util.Random; + +public class HexBiomeChunk { + private static final short SIDE = 32; + private static final short SIZE = SIDE * SIDE; + private static final byte SIDE_MASK = SIDE - 1; + private static final byte SIDE_OFFSET = (byte) Math.round(Math.log(SIDE) / Math.log(2)); + private static final short[][] NEIGHBOURS; + public static final short SCALE = SIDE / 4; + + private final BCLBiome[] biomes; + + public HexBiomeChunk(Random random, BiomePicker picker) { + BCLBiome[][] buffers = new BCLBiome[2][SIZE]; + + byte scale = SIDE / 4; + for (byte x = 0; x < 4; x++) { + for (byte z = 0; z < 4; z++) { + byte px = (byte) (x * scale + random.nextInt(scale)); + byte pz = (byte) (z * scale + random.nextInt(scale)); + circle(buffers[0], getIndex(px, pz), picker.getBiome(random), null); + } + } + + short maxSide = SIZE - SIDE; + boolean hasEmptyCells = true; + byte bufferIndex = 0; + while (hasEmptyCells) { + BCLBiome[] inBuffer = buffers[bufferIndex]; + bufferIndex = (byte) ((bufferIndex + 1) & 1); + BCLBiome[] outBuffer = buffers[bufferIndex]; + hasEmptyCells = false; + + for (short index = SIDE; index < maxSide; index++) { + byte z = (byte) (index & SIDE_MASK); + if (z == 0 || z == SIDE_MASK) { + continue; + } + if (inBuffer[index] != null) { + outBuffer[index] = inBuffer[index]; + short[] neighbours = getNeighbours(index & SIDE_MASK); + short indexSide = (short) (index + neighbours[random.nextInt(6)]); + if (indexSide >= 0 && indexSide < SIZE && outBuffer[indexSide] == null) { + outBuffer[indexSide] = inBuffer[index]; + } + } + else { + hasEmptyCells = true; + } + } + } + + BCLBiome[] outBuffer = buffers[bufferIndex]; + byte preN = (byte) (SIDE_MASK - 2); + for (byte index = 0; index < SIDE; index++) { + outBuffer[getIndex(index, (byte) 0)] = outBuffer[getIndex(index, (byte) 2)]; + outBuffer[getIndex((byte) 0, index)] = outBuffer[getIndex((byte) 2, index)]; + outBuffer[getIndex(index, SIDE_MASK)] = outBuffer[getIndex(index, preN)]; + outBuffer[getIndex(SIDE_MASK, index)] = outBuffer[getIndex(preN, index)]; + } + + for (short index = 0; index < SIZE; index++) { + if (random.nextInt(4) == 0) { + circle(outBuffer, index, outBuffer[index].getSubBiome(random), outBuffer[index]); + } + } + + this.biomes = outBuffer; + } + + private void circle(BCLBiome[] buffer, short center, BCLBiome biome, BCLBiome mask) { + if (buffer[center] == mask) { + buffer[center] = biome; + } + short[] neighbours = getNeighbours(center & SIDE_MASK); + for (short i: neighbours) { + short index = (short) (center + i); + if (index >= 0 && index < SIZE && buffer[index] == mask) { + buffer[index] = biome; + } + } + } + + private static byte wrap(int value) { + return (byte) (value & SIDE_MASK); + } + + private short getIndex(byte x, byte z) { + return (short) ((short) x << SIDE_OFFSET | z); + } + + public BCLBiome getBiome(int x, int z) { + return biomes[getIndex(wrap(x), wrap(z))]; + } + + public static int scaleCoordinate(int value) { + return value >> SIDE_OFFSET; + } + + public static boolean isBorder(int value) { + return wrap(value) == SIDE_MASK; + } + + private short[] getNeighbours(int z) { + return NEIGHBOURS[z & 1]; + } + + static { + NEIGHBOURS = new short[2][6]; + + NEIGHBOURS[0][0] = 1; + NEIGHBOURS[0][1] = -1; + NEIGHBOURS[0][2] = SIDE; + NEIGHBOURS[0][3] = -SIDE; + NEIGHBOURS[0][4] = SIDE + 1; + NEIGHBOURS[0][5] = SIDE - 1; + + NEIGHBOURS[1][0] = 1; + NEIGHBOURS[1][1] = -1; + NEIGHBOURS[1][2] = SIDE; + NEIGHBOURS[1][3] = -SIDE; + NEIGHBOURS[1][4] = -SIDE + 1; + NEIGHBOURS[1][5] = -SIDE - 1; + } +} diff --git a/src/main/java/ru/bclib/world/generator/map/hex/HexBiomeMap.java b/src/main/java/ru/bclib/world/generator/map/hex/HexBiomeMap.java new file mode 100644 index 00000000..0eafcad3 --- /dev/null +++ b/src/main/java/ru/bclib/world/generator/map/hex/HexBiomeMap.java @@ -0,0 +1,156 @@ +package ru.bclib.world.generator.map.hex; + +import ru.bclib.noise.OpenSimplexNoise; +import ru.bclib.util.MHelper; +import ru.bclib.world.biomes.BCLBiome; +import ru.bclib.world.generator.BiomePicker; + +import java.awt.Point; +import java.util.HashMap; +import java.util.Random; + +public class HexBiomeMap { + private static final float RAD_INNER = (float) Math.sqrt(3.0) * 0.5F; + private static final float COEF = 0.25F * (float) Math.sqrt(3.0); + private static final float COEF_HALF = COEF * 0.5F; + private static final float SIN = (float) Math.sin(0.4); + private static final float COS = (float) Math.cos(0.4); + private static final Random RANDOM = new Random(); + private static final float[] EDGE_CIRCLE_X; + private static final float[] EDGE_CIRCLE_Z; + + private final HashMap chunks = new HashMap<>(); + private final Point selector = new Point(); + private final BiomePicker picker; + + private final OpenSimplexNoise[] noises = new OpenSimplexNoise[2]; + private final byte noiseIterations; + private final float scale; + private final int seed; + + public HexBiomeMap(long seed, int size, BiomePicker picker) { + this.picker = picker; + this.scale = (float) size / HexBiomeChunk.SCALE; + Random random = new Random(seed); + noises[0] = new OpenSimplexNoise(random.nextInt()); + noises[1] = new OpenSimplexNoise(random.nextInt()); + noiseIterations = (byte) Math.min(Math.ceil(Math.log(scale) / Math.log(2)), 5); + this.seed = (int) (seed & 0xFFFFFFFF); + } + + public void clearCache() { + if (chunks.size() > 127) { + chunks.clear(); + } + } + + public BCLBiome getBiome(double x, double z) { + BCLBiome BCLBiome = getRawBiome(x, z); + if (BCLBiome.getEdge() != null) { + float offset = scale * BCLBiome.getEdgeSize(); + for (byte i = 0; i < 8; i++) { + if (getRawBiome(x + offset * EDGE_CIRCLE_X[i], z + offset * EDGE_CIRCLE_Z[i]) != BCLBiome) { + return BCLBiome.getEdge(); + } + } + } + return BCLBiome; + } + + private BCLBiome getRawBiome(double x, double z) { + double px = x / scale * RAD_INNER; + double pz = z / scale; + double dx = rotateX(px, pz); + double dz = rotateZ(px, pz); + px = dx; + pz = dz; + + dx = getNoise(px, pz, (byte) 0) * 0.2F; + dz = getNoise(pz, px, (byte) 1) * 0.2F; + px += dx; + pz += dz; + + int cellZ = (int) Math.floor(pz); + boolean offset = (cellZ & 1) == 1; + + if (offset) { + px += 0.5; + } + + int cellX = (int) Math.floor(px); + + float pointX = (float) (px - cellX - 0.5); + float pointZ = (float) (pz - cellZ - 0.5); + + if (Math.abs(pointZ) < 0.3333F) { + return getChunkBiome(cellX, cellZ); + } + + if (insideHexagon(0, 0, 1.1555F, pointZ * RAD_INNER, pointX)) { + return getChunkBiome(cellX, cellZ); + } + + cellX = pointX < 0 ? (offset ? cellX - 1 : cellX) : (offset ? cellX : cellX + 1); + cellZ = pointZ < 0 ? cellZ - 1 : cellZ + 1; + + return getChunkBiome(cellX, cellZ); + } + + private BCLBiome getChunkBiome(int x, int z) { + int cx = HexBiomeChunk.scaleCoordinate(x); + int cz = HexBiomeChunk.scaleCoordinate(z); + + if (((z >> 2) & 1) == 0 && HexBiomeChunk.isBorder(x)) { + x = 0; + cx += 1; + } + else if (((x >> 2) & 1) == 0 && HexBiomeChunk.isBorder(z)) { + z = 0; + cz += 1; + } + + selector.setLocation(cx, cz); + HexBiomeChunk chunk = chunks.get(selector); + if (chunk == null) { + RANDOM.setSeed(MHelper.getSeed(seed, cx, cz)); + chunk = new HexBiomeChunk(RANDOM, picker); + chunks.put(new Point(selector), chunk); + } + return chunk.getBiome(x, z); + } + + private boolean insideHexagon(float centerX, float centerZ, float radius, float x, float z) { + double dx = Math.abs(x - centerX) / radius; + double dy = Math.abs(z - centerZ) / radius; + return (dy <= COEF) && (COEF * dx + 0.25F * dy <= COEF_HALF); + } + + private double getNoise(double x, double z, byte state) { + double result = 0; + for (byte i = 1; i <= noiseIterations; i++) { + OpenSimplexNoise noise = noises[state]; + state = (byte) ((state + 1) & 1); + result += noise.eval(x * i, z * i) / i; + } + return result; + } + + private double rotateX(double x, double z) { + return x * COS - z * SIN; + } + + private double rotateZ(double x, double z) { + return x * SIN + z * COS; + } + + static { + EDGE_CIRCLE_X = new float[8]; + EDGE_CIRCLE_Z = new float[8]; + + for (byte i = 0; i < 8; i++) { + float angle = i / 4F * (float) Math.PI; + EDGE_CIRCLE_X[i] = (float) Math.sin(angle); + EDGE_CIRCLE_Z[i] = (float) Math.cos(angle); + } + } +} diff --git a/src/main/java/ru/bclib/world/generator/BiomeChunk.java b/src/main/java/ru/bclib/world/generator/map/square/SquareBiomeChunk.java similarity index 86% rename from src/main/java/ru/bclib/world/generator/BiomeChunk.java rename to src/main/java/ru/bclib/world/generator/map/square/SquareBiomeChunk.java index d424462e..9b057ffd 100644 --- a/src/main/java/ru/bclib/world/generator/BiomeChunk.java +++ b/src/main/java/ru/bclib/world/generator/map/square/SquareBiomeChunk.java @@ -1,54 +1,56 @@ -package ru.bclib.world.generator; - -import ru.bclib.world.biomes.BCLBiome; - -import java.util.Random; - -public class BiomeChunk { - private static final int BIT_OFFSET = 4; - protected static final int WIDTH = 1 << BIT_OFFSET; - private static final int SM_WIDTH = WIDTH >> 1; - private static final int SM_BIT_OFFSET = BIT_OFFSET >> 1; - private static final int MASK_OFFSET = SM_WIDTH - 1; - protected static final int MASK_WIDTH = WIDTH - 1; - - private static final int SM_CAPACITY = SM_WIDTH * SM_WIDTH; - private static final int CAPACITY = WIDTH * WIDTH; - - private final BCLBiome[] biomes; - - public BiomeChunk(BiomeMap map, Random random, BiomePicker picker) { - BCLBiome[] PreBio = new BCLBiome[SM_CAPACITY]; - biomes = new BCLBiome[CAPACITY]; - - for (int x = 0; x < SM_WIDTH; x++) { - int offset = x << SM_BIT_OFFSET; - for (int z = 0; z < SM_WIDTH; z++) { - PreBio[offset | z] = picker.getBiome(random); - } - } - - for (int x = 0; x < WIDTH; x++) { - int offset = x << BIT_OFFSET; - for (int z = 0; z < WIDTH; z++) { - biomes[offset | z] = PreBio[getSmIndex(offsetXZ(x, random), offsetXZ(z, random))].getSubBiome(random); - } - } - } - - public BCLBiome getBiome(int x, int z) { - return biomes[getIndex(x & MASK_WIDTH, z & MASK_WIDTH)]; - } - - private int offsetXZ(int x, Random random) { - return ((x + random.nextInt(2)) >> 1) & MASK_OFFSET; - } - - private int getIndex(int x, int z) { - return x << BIT_OFFSET | z; - } - - private int getSmIndex(int x, int z) { - return x << SM_BIT_OFFSET | z; - } -} +package ru.bclib.world.generator.map.square; + +import ru.bclib.world.biomes.BCLBiome; +import ru.bclib.world.generator.BiomePicker; + +import java.util.Random; + +@Deprecated +public class SquareBiomeChunk { + private static final int BIT_OFFSET = 4; + protected static final int WIDTH = 1 << BIT_OFFSET; + private static final int SM_WIDTH = WIDTH >> 1; + private static final int SM_BIT_OFFSET = BIT_OFFSET >> 1; + private static final int MASK_OFFSET = SM_WIDTH - 1; + protected static final int MASK_WIDTH = WIDTH - 1; + + private static final int SM_CAPACITY = SM_WIDTH * SM_WIDTH; + private static final int CAPACITY = WIDTH * WIDTH; + + private final BCLBiome[] biomes; + + public SquareBiomeChunk(SquareBiomeMap map, Random random, BiomePicker picker) { + BCLBiome[] PreBio = new BCLBiome[SM_CAPACITY]; + biomes = new BCLBiome[CAPACITY]; + + for (int x = 0; x < SM_WIDTH; x++) { + int offset = x << SM_BIT_OFFSET; + for (int z = 0; z < SM_WIDTH; z++) { + PreBio[offset | z] = picker.getBiome(random); + } + } + + for (int x = 0; x < WIDTH; x++) { + int offset = x << BIT_OFFSET; + for (int z = 0; z < WIDTH; z++) { + biomes[offset | z] = PreBio[getSmIndex(offsetXZ(x, random), offsetXZ(z, random))].getSubBiome(random); + } + } + } + + public BCLBiome getBiome(int x, int z) { + return biomes[getIndex(x & MASK_WIDTH, z & MASK_WIDTH)]; + } + + private int offsetXZ(int x, Random random) { + return ((x + random.nextInt(2)) >> 1) & MASK_OFFSET; + } + + private int getIndex(int x, int z) { + return x << BIT_OFFSET | z; + } + + private int getSmIndex(int x, int z) { + return x << SM_BIT_OFFSET | z; + } +} diff --git a/src/main/java/ru/bclib/world/generator/BiomeMap.java b/src/main/java/ru/bclib/world/generator/map/square/SquareBiomeMap.java similarity index 80% rename from src/main/java/ru/bclib/world/generator/BiomeMap.java rename to src/main/java/ru/bclib/world/generator/map/square/SquareBiomeMap.java index 34251623..022366c4 100644 --- a/src/main/java/ru/bclib/world/generator/BiomeMap.java +++ b/src/main/java/ru/bclib/world/generator/map/square/SquareBiomeMap.java @@ -1,113 +1,115 @@ -package ru.bclib.world.generator; - -import com.google.common.collect.Maps; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.levelgen.LegacyRandomSource; -import net.minecraft.world.level.levelgen.WorldgenRandom; -import ru.bclib.noise.OpenSimplexNoise; -import ru.bclib.util.MHelper; -import ru.bclib.world.biomes.BCLBiome; - -import java.util.Map; - -public class BiomeMap { - private final WorldgenRandom RANDOM; - - private final Map maps = Maps.newHashMap(); - private final int size; - private final int sizeXZ; - private final int depth; - private final OpenSimplexNoise noiseX; - private final OpenSimplexNoise noiseZ; - private final BiomePicker picker; - private final long seed; - - public BiomeMap(long seed, int size, BiomePicker picker) { - maps.clear(); - RANDOM = new WorldgenRandom(new LegacyRandomSource(seed)); - noiseX = new OpenSimplexNoise(RANDOM.nextLong()); - noiseZ = new OpenSimplexNoise(RANDOM.nextLong()); - this.sizeXZ = size; - depth = (int) Math.ceil(Math.log(size) / Math.log(2)) - 2; - this.size = 1 << depth; - this.picker = picker; - this.seed = seed; - } - - public long getSeed() { - return seed; - } - - public void clearCache() { - if (maps.size() > 32) { - maps.clear(); - } - } - - private BCLBiome getRawBiome(int bx, int bz) { - double x = (double) bx * size / sizeXZ; - double z = (double) bz * size / sizeXZ; - double nx = x; - double nz = z; - - double px = bx * 0.2; - double pz = bz * 0.2; - - for (int i = 0; i < depth; i++) { - nx = (x + noiseX.eval(px, pz)) / 2F; - nz = (z + noiseZ.eval(px, pz)) / 2F; - - x = nx; - z = nz; - - px = px / 2 + i; - pz = pz / 2 + i; - } - - bx = MHelper.floor(x); - bz = MHelper.floor(z); - if ((bx & BiomeChunk.MASK_WIDTH) == BiomeChunk.MASK_WIDTH) { - x += (bz / 2) & 1; - } - if ((bz & BiomeChunk.MASK_WIDTH) == BiomeChunk.MASK_WIDTH) { - z += (bx / 2) & 1; - } - - ChunkPos cpos = new ChunkPos(MHelper.floor(x / BiomeChunk.WIDTH), MHelper.floor(z / BiomeChunk.WIDTH)); - BiomeChunk chunk = maps.get(cpos); - if (chunk == null) { - RANDOM.setLargeFeatureWithSalt(0, cpos.x, cpos.z, 0); - chunk = new BiomeChunk(this, RANDOM, picker); - maps.put(cpos, chunk); - } - - return chunk.getBiome(MHelper.floor(x), MHelper.floor(z)); - } - - public BCLBiome getBiome(int x, int z) { - BCLBiome biome = getRawBiome(x, z); - - if (biome.getEdge() != null || (biome.getParentBiome() != null && biome.getParentBiome().getEdge() != null)) { - BCLBiome search = biome; - if (biome.getParentBiome() != null) { - search = biome.getParentBiome(); - } - int d = (int) Math.ceil(search.getEdgeSize() / 4F) << 2; - - boolean edge = !search.isSame(getRawBiome(x + d, z)); - edge = edge || !search.isSame(getRawBiome(x - d, z)); - edge = edge || !search.isSame(getRawBiome(x, z + d)); - edge = edge || !search.isSame(getRawBiome(x, z - d)); - edge = edge || !search.isSame(getRawBiome(x - 1, z - 1)); - edge = edge || !search.isSame(getRawBiome(x - 1, z + 1)); - edge = edge || !search.isSame(getRawBiome(x + 1, z - 1)); - edge = edge || !search.isSame(getRawBiome(x + 1, z + 1)); - - if (edge) { - biome = search.getEdge(); - } - } - - return biome; - } -} +package ru.bclib.world.generator.map.square; + +import com.google.common.collect.Maps; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.levelgen.LegacyRandomSource; +import net.minecraft.world.level.levelgen.WorldgenRandom; +import ru.bclib.noise.OpenSimplexNoise; +import ru.bclib.util.MHelper; +import ru.bclib.world.biomes.BCLBiome; +import ru.bclib.world.generator.BiomePicker; + +import java.util.Map; + +@Deprecated +public class SquareBiomeMap { + private final WorldgenRandom RANDOM; + + private final Map maps = Maps.newHashMap(); + private final int size; + private final int sizeXZ; + private final int depth; + private final OpenSimplexNoise noiseX; + private final OpenSimplexNoise noiseZ; + private final BiomePicker picker; + private final long seed; + + public SquareBiomeMap(long seed, int size, BiomePicker picker) { + maps.clear(); + RANDOM = new WorldgenRandom(new LegacyRandomSource(seed)); + noiseX = new OpenSimplexNoise(RANDOM.nextLong()); + noiseZ = new OpenSimplexNoise(RANDOM.nextLong()); + this.sizeXZ = size; + depth = (int) Math.ceil(Math.log(size) / Math.log(2)) - 2; + this.size = 1 << depth; + this.picker = picker; + this.seed = seed; + } + + public long getSeed() { + return seed; + } + + public void clearCache() { + if (maps.size() > 32) { + maps.clear(); + } + } + + private BCLBiome getRawBiome(int bx, int bz) { + double x = (double) bx * size / sizeXZ; + double z = (double) bz * size / sizeXZ; + double nx = x; + double nz = z; + + double px = bx * 0.2; + double pz = bz * 0.2; + + for (int i = 0; i < depth; i++) { + nx = (x + noiseX.eval(px, pz)) / 2F; + nz = (z + noiseZ.eval(px, pz)) / 2F; + + x = nx; + z = nz; + + px = px / 2 + i; + pz = pz / 2 + i; + } + + bx = MHelper.floor(x); + bz = MHelper.floor(z); + if ((bx & SquareBiomeChunk.MASK_WIDTH) == SquareBiomeChunk.MASK_WIDTH) { + x += (bz / 2) & 1; + } + if ((bz & SquareBiomeChunk.MASK_WIDTH) == SquareBiomeChunk.MASK_WIDTH) { + z += (bx / 2) & 1; + } + + ChunkPos cpos = new ChunkPos(MHelper.floor(x / SquareBiomeChunk.WIDTH), MHelper.floor(z / SquareBiomeChunk.WIDTH)); + SquareBiomeChunk chunk = maps.get(cpos); + if (chunk == null) { + RANDOM.setLargeFeatureWithSalt(0, cpos.x, cpos.z, 0); + chunk = new SquareBiomeChunk(this, RANDOM, picker); + maps.put(cpos, chunk); + } + + return chunk.getBiome(MHelper.floor(x), MHelper.floor(z)); + } + + public BCLBiome getBiome(int x, int z) { + BCLBiome biome = getRawBiome(x, z); + + if (biome.getEdge() != null || (biome.getParentBiome() != null && biome.getParentBiome().getEdge() != null)) { + BCLBiome search = biome; + if (biome.getParentBiome() != null) { + search = biome.getParentBiome(); + } + int d = (int) Math.ceil(search.getEdgeSize() / 4F) << 2; + + boolean edge = !search.isSame(getRawBiome(x + d, z)); + edge = edge || !search.isSame(getRawBiome(x - d, z)); + edge = edge || !search.isSame(getRawBiome(x, z + d)); + edge = edge || !search.isSame(getRawBiome(x, z - d)); + edge = edge || !search.isSame(getRawBiome(x - 1, z - 1)); + edge = edge || !search.isSame(getRawBiome(x - 1, z + 1)); + edge = edge || !search.isSame(getRawBiome(x + 1, z - 1)); + edge = edge || !search.isSame(getRawBiome(x + 1, z + 1)); + + if (edge) { + biome = search.getEdge(); + } + } + + return biome; + } +}