WorldGenerator gen 1
This commit is contained in:
@@ -3,9 +3,13 @@ package mc.core.block;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
public enum BlockType {
|
public enum BlockType {
|
||||||
DIRT(1, "Dirt"),
|
STONE(1, "Stone"),
|
||||||
GRASS(2, "Grass"),
|
GRASS(2, "Grass"),
|
||||||
BEDROCK(7, "Bedrock");
|
DIRT(3, "Dirt"),
|
||||||
|
BEDROCK(7, "Bedrock"),
|
||||||
|
WATER(8, "Water"),
|
||||||
|
SAND(12, "Sand"),
|
||||||
|
SNOW(32, "Snow");
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final int id;
|
private final int id;
|
||||||
|
|||||||
@@ -3,37 +3,41 @@ package mc.core.world;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
public enum Biome {
|
public enum Biome {
|
||||||
OCEAN(0, "Ocean"),
|
OCEAN(0, "Ocean", 0x000080),
|
||||||
PLAINS(1, "Plains"),
|
PLAINS(1, "Plains", 0x008000),
|
||||||
DESERT(2, "Desert"),
|
DESERT(2, "Desert", 0xbdb76b),
|
||||||
EXTREME_HILLS(3, "Extreme hills"),
|
EXTREME_HILLS(3, "Extreme hills", 0xffffff),
|
||||||
FOREST(4, "Forest"),
|
FOREST(4, "Forest", 0x006400),
|
||||||
TAIGA(5, "Taiga"),
|
TAIGA(5, "Taiga", 0xf0f8ff),
|
||||||
SWAMPLAND(6, "Swampland"),
|
SWAMPLAND(6, "Swampland", 0x808000),
|
||||||
RIVER(7, "River"),
|
RIVER(7, "River", 0xffffff),
|
||||||
HELL(8, "Hell"),
|
HELL(8, "Hell", 0xffffff),
|
||||||
SKY(9, "Sky"),
|
SKY(9, "Sky", 0xffffff),
|
||||||
FROZEN_OCEAN(10, "Frozen ocean"),
|
FROZEN_OCEAN(10, "Frozen ocean", 0xe0ffff),
|
||||||
FROZEN_RIVER(11, "Frozen river"),
|
FROZEN_RIVER(11, "Frozen river", 0xffffff),
|
||||||
ICE_PLAINS(12, "Ice plains"),
|
ICE_PLAINS(12, "Ice plains", 0xfffafa),
|
||||||
ICE_MOUNTAINS(13, "Ice mountains"),
|
ICE_MOUNTAINS(13, "Ice mountains", 0xfffafa),
|
||||||
MUSHROOM_ISLAND(14, "Mushroom island"),
|
MUSHROOM_ISLAND(14, "Mushroom island", 0xffffff),
|
||||||
MUSHROOM_ISLAND_SHORE(15, "Mushroom island shore"),
|
MUSHROOM_ISLAND_SHORE(15, "Mushroom island shore", 0xffffff),
|
||||||
BEACH(16, "Beach"),
|
BEACH(16, "Beach", 0xffffff),
|
||||||
DESERT_HILLS(17, "Desert hills"),
|
DESERT_HILLS(17, "Desert hills", 0xbdb76b),
|
||||||
FOREST_HILLS(18, "Forest hills"),
|
FOREST_HILLS(18, "Forest hills", 0x006400),
|
||||||
TAIGA_HILLS(19, "Taiga hills"),
|
TAIGA_HILLS(19, "Taiga hills", 0xf0f8ff),
|
||||||
EXTREME_HILLS_EDGE(20, "Extreme hills edge"),
|
EXTREME_HILLS_EDGE(20, "Extreme hills edge", 0xffffff),
|
||||||
JUNGLE(21, "Jungle"),
|
JUNGLE(21, "Jungle", 0xadff2f),
|
||||||
JUNGLE_HILLS(22, "Jungle hills");
|
JUNGLE_HILLS(22, "Jungle hills", 0xadff2f),
|
||||||
|
DEEP_OCEAN(23, "Deep ocean", 0x191970);
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final int id;
|
private final int id;
|
||||||
@Getter
|
@Getter
|
||||||
private final String name;
|
private final String name;
|
||||||
|
@Getter
|
||||||
|
private final int color;
|
||||||
|
|
||||||
Biome(int id, String name) {
|
Biome(int id, String name, int color) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.color = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ public interface Region {
|
|||||||
void setChunk(int x, int y, int z, Chunk chunk);
|
void setChunk(int x, int y, int z, Chunk chunk);
|
||||||
|
|
||||||
int getX();
|
int getX();
|
||||||
int getY();
|
|
||||||
int getZ();
|
int getZ();
|
||||||
|
|
||||||
Biome getBiomeAt (int x, int z);
|
Biome getBiomeAt (int x, int z);
|
||||||
|
|||||||
@@ -49,5 +49,8 @@ public interface World {
|
|||||||
Chunk getChunk(int x, int y, int z);
|
Chunk getChunk(int x, int y, int z);
|
||||||
void setChunk(int x, int y, int z, Chunk chunk);
|
void setChunk(int x, int y, int z, Chunk chunk);
|
||||||
|
|
||||||
long getSeed ();
|
Region getRegion (int x, int z);
|
||||||
|
void setRegion (int x, int z, Region region);
|
||||||
|
|
||||||
|
int getSeed ();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,7 @@ package mc.world.flat;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import mc.core.Location;
|
import mc.core.Location;
|
||||||
import mc.core.world.Chunk;
|
import mc.core.world.*;
|
||||||
import mc.core.world.IWorldType;
|
|
||||||
import mc.core.world.World;
|
|
||||||
import mc.core.world.WorldType;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@@ -40,7 +37,17 @@ public class FlatWorld implements World {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getSeed() {
|
public Region getRegion(int x, int z) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRegion(int x, int z, Region region) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSeed() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package mc.world.generated_world;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import mc.core.Location;
|
||||||
|
import mc.core.world.*;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class CubicWorld implements World {
|
||||||
|
@Getter
|
||||||
|
private final UUID worldId;
|
||||||
|
private final int seed;
|
||||||
|
private volatile Location spawnLocation;
|
||||||
|
private final transient Object spawnLocationLock = new Object();
|
||||||
|
private final transient ChunkLoader chunkLoader;
|
||||||
|
|
||||||
|
public CubicWorld(UUID worldId, int seed) {
|
||||||
|
this.worldId = worldId;
|
||||||
|
chunkLoader = new InMemoryCacheChunkLoader(worldId);
|
||||||
|
this.seed = seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CubicWorld(int seed) {
|
||||||
|
this.worldId = UUID.randomUUID();
|
||||||
|
chunkLoader = new InMemoryCacheChunkLoader(worldId);
|
||||||
|
this.seed = seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CubicWorld(UUID worldId) {
|
||||||
|
this.worldId = worldId;
|
||||||
|
chunkLoader = new InMemoryCacheChunkLoader(worldId);
|
||||||
|
this.seed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CubicWorld () {
|
||||||
|
this.worldId = UUID.randomUUID();
|
||||||
|
chunkLoader = new InMemoryCacheChunkLoader(worldId);
|
||||||
|
this.seed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IWorldType getWorldType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Location getSpawn() {
|
||||||
|
if (spawnLocation == null) {
|
||||||
|
synchronized (spawnLocationLock) {
|
||||||
|
if (spawnLocation == null) {
|
||||||
|
spawnLocation = Location.startPointLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return spawnLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSpawn(Location location) {
|
||||||
|
synchronized (spawnLocationLock) {
|
||||||
|
this.spawnLocation = location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Chunk getChunk(int x, int y, int z) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setChunk(int x, int y, int z, Chunk chunk) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Region getRegion(int x, int z) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRegion(int x, int z, Region region) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSeed() {
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,390 @@
|
|||||||
|
package mc.world.generated_world;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import mc.core.block.BlockFactory;
|
||||||
|
import mc.core.block.BlockType;
|
||||||
|
import mc.core.world.*;
|
||||||
|
import mc.world.generated_world.region.RegionImpl;
|
||||||
|
import mc.world.generated_world.word.Temperature;
|
||||||
|
import mc.world.generated_world.word.Wetness;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class SeedBasedWorldGenerator implements WorldGenerator {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception{
|
||||||
|
WorldGenerator worldGenerator = new SeedBasedWorldGenerator();
|
||||||
|
World world = new CubicWorld(UUID.fromString("00000000-0000-0000-C000-000000000046"), 123);
|
||||||
|
worldGenerator.generateRegion(0, 0, world);
|
||||||
|
worldGenerator.generateRegion(1, 0, world);
|
||||||
|
worldGenerator.generateRegion(-1, 0, world);
|
||||||
|
worldGenerator.generateRegion(0, 1, world);
|
||||||
|
worldGenerator.generateRegion(0, -1, world);
|
||||||
|
BufferedImage image = new BufferedImage(3 * 256, 3 * 256, BufferedImage.TYPE_INT_RGB);
|
||||||
|
BufferedImage currentImage;
|
||||||
|
int shiftX;
|
||||||
|
int shiftY;
|
||||||
|
currentImage = ImageIO.read(new File("out/0.0", "biomeMap.png"));
|
||||||
|
shiftX = 1;
|
||||||
|
shiftY = 1;
|
||||||
|
for (int x = 0; x < 256; x ++){
|
||||||
|
for (int y = 0; y < 256; y ++){
|
||||||
|
int tx = 256 * shiftX + x;
|
||||||
|
int ty = 256 * shiftY + y;
|
||||||
|
image.setRGB(tx, ty, currentImage.getRGB(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentImage = ImageIO.read(new File("out/0.1", "biomeMap.png"));
|
||||||
|
shiftX = 1;
|
||||||
|
shiftY = 2;
|
||||||
|
for (int x = 0; x < 256; x ++){
|
||||||
|
for (int y = 0; y < 256; y ++){
|
||||||
|
int tx = 256 * shiftX + x;
|
||||||
|
int ty = 256 * shiftY + y;
|
||||||
|
image.setRGB(tx, ty, currentImage.getRGB(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentImage = ImageIO.read(new File("out/1.0", "biomeMap.png"));
|
||||||
|
shiftX = 2;
|
||||||
|
shiftY = 1;
|
||||||
|
for (int x = 0; x < 256; x ++){
|
||||||
|
for (int y = 0; y < 256; y ++){
|
||||||
|
int tx = 256 * shiftX + x;
|
||||||
|
int ty = 256 * shiftY + y;
|
||||||
|
image.setRGB(tx, ty, currentImage.getRGB(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentImage = ImageIO.read(new File("out/-1.0", "biomeMap.png"));
|
||||||
|
shiftX = 0;
|
||||||
|
shiftY = 1;
|
||||||
|
for (int x = 0; x < 256; x ++){
|
||||||
|
for (int y = 0; y < 256; y ++){
|
||||||
|
int tx = 256 * shiftX + x;
|
||||||
|
int ty = 256 * shiftY + y;
|
||||||
|
image.setRGB(tx, ty, currentImage.getRGB(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentImage = ImageIO.read(new File("out/0.-1", "biomeMap.png"));
|
||||||
|
shiftX = 1;
|
||||||
|
shiftY = 0;
|
||||||
|
for (int x = 0; x < 256; x ++){
|
||||||
|
for (int y = 0; y < 256; y ++){
|
||||||
|
int tx = 256 * shiftX + x;
|
||||||
|
int ty = 256 * shiftY + y;
|
||||||
|
image.setRGB(tx, ty, currentImage.getRGB(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImageIO.write(image, "png", new File("out", "merged.png"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Region generateRegion(int x, int z, World world) {
|
||||||
|
Region region = new RegionImpl(x,z);
|
||||||
|
RegionGenerator regionGenerator = new RegionGenerator(world, region);
|
||||||
|
regionGenerator.generate();
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
private class RegionGenerator {
|
||||||
|
private final World world;
|
||||||
|
private final Region region;
|
||||||
|
private final int size = 256;
|
||||||
|
private NoiseGenerator noiseGenerator;
|
||||||
|
private BlockFactory blockFactory;
|
||||||
|
|
||||||
|
private double sigmoid (double x) {
|
||||||
|
x -= 0.5;
|
||||||
|
x *= 15;
|
||||||
|
return 1.0 / (1.0 + Math.exp(-x));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int convert (int x) {
|
||||||
|
return 40960 + x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generate() {
|
||||||
|
noiseGenerator = new NoiseGenerator(world.getSeed());
|
||||||
|
noiseGenerator.init();
|
||||||
|
File file = new File("out", region.getX() + "." + region.getZ());
|
||||||
|
file.mkdirs();
|
||||||
|
int[][] heightMap = new int[size][size];
|
||||||
|
int[][] grassMap = new int[size][size];
|
||||||
|
int[][] temperatureMap = new int[size][size];
|
||||||
|
int[][] wetMap = new int[size][size];
|
||||||
|
Biome[][] biomes = new Biome[size][size];
|
||||||
|
for (int x = 0; x < size; x ++) {
|
||||||
|
for (int z = 0; z < size; z ++) {
|
||||||
|
int tx = convert(x + region.getX() * 256);
|
||||||
|
int tz = convert(z + region.getZ() * 256);
|
||||||
|
double p = sigmoid(noiseGenerator.noise(tx / 53d, tz / 53d));
|
||||||
|
double r = Math.sqrt(noiseGenerator.noise(tx / 6d, tz / 6d));
|
||||||
|
double h = (WorldConstants.WORLD_MAX_HEIGHT - WorldConstants.WORLD_MIN_HEIGHT) * Math.min(p * r, 1);
|
||||||
|
h = Math.min(WorldConstants.WORLD_MAX_HEIGHT, h + WorldConstants.WORLD_MIN_HEIGHT);
|
||||||
|
heightMap[x][z] = (int)(h);
|
||||||
|
grassMap[x][z] = (int) (1 + SeedRandomGenerator.random(tx, tz, world.getSeed()) * (WorldConstants.LANDFILL_GRASS_SURFACE_THIN - 1));
|
||||||
|
double k = Math.sqrt(noiseGenerator.noise(tx * 2.99, tz * 2.99));
|
||||||
|
double q = Math.sqrt(noiseGenerator.noise(tx / 41.0, tz / 41.0));
|
||||||
|
temperatureMap[x][z] = (int) (100 * Math.min((k * k + q * q + k * q) * k * q, 0.99));
|
||||||
|
if (heightMap[x][z] < WorldConstants.WORLD_SEA_LEVEL) {
|
||||||
|
biomes[x][z] = Biome.OCEAN;
|
||||||
|
wetMap[x][z] = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int x = 1; x < size - 1; x ++) {
|
||||||
|
for (int z = 1; z < size - 1; z++) {
|
||||||
|
int mid = 0;
|
||||||
|
for (int tx = x - 1; tx <= x + 1; tx ++) {
|
||||||
|
for (int tz = z - 1; tz <= z + 1; tz ++) {
|
||||||
|
mid += wetMap[tx][tz];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wetMap[x][z] = mid / 9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int z = 1; z < size - 1; z++) {
|
||||||
|
for (int x = 1; x < size - 1; x ++) {
|
||||||
|
int mid = 0;
|
||||||
|
for (int tx = x - 1; tx <= x + 1; tx ++) {
|
||||||
|
for (int tz = z - 1; tz <= z + 1; tz ++) {
|
||||||
|
mid += wetMap[tx][tz];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wetMap[x][z] = (int) (mid / 9 * (1 + 0.1 * SeedRandomGenerator.random(x, z, world.getSeed())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int z = 1; z < size - 1; z++) {
|
||||||
|
for (int x = 1; x < size - 1; x ++) {
|
||||||
|
wetMap[x][z] = (int) Math.min(100, 60 * noiseGenerator.noise(x / 31d, z / 67d) + wetMap[x][z] * (1 + 0.2 * SeedRandomGenerator.random(x, z, world.getSeed())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
smooth(grassMap);
|
||||||
|
smooth(temperatureMap);
|
||||||
|
smooth(wetMap);
|
||||||
|
//smooth(heightMap);
|
||||||
|
|
||||||
|
for (int x = 0; x < 256; x ++) {
|
||||||
|
for (int z = 0; z < 256; z ++) {
|
||||||
|
Temperature temperature = Temperature.values()[temperatureMap[x][z] / 20];
|
||||||
|
Wetness wetness = Wetness.values()[(Math.min(wetMap[x][z], 100) - 1) / 100 * Wetness.values().length];
|
||||||
|
biomes[x][z] = selectBiome(temperature, wetness, heightMap[x][z]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================ DEBUG =======================================
|
||||||
|
try {
|
||||||
|
BufferedImage image = new BufferedImage(256, 256, BufferedImage.TYPE_INT_RGB);
|
||||||
|
BufferedImage subImage = new BufferedImage(256, 256, BufferedImage.TYPE_INT_RGB);
|
||||||
|
for (int x = 0; x < 256; x ++) {
|
||||||
|
for (int z = 0; z < 256; z ++) {
|
||||||
|
int h = heightMap[x][z];
|
||||||
|
h = h << 16 | h << 8 | h;
|
||||||
|
image.setRGB(x, z, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImageIO.write(image, "png", new File("out/" + region.getX() + "." +region.getZ() +"/hightmap.png"));
|
||||||
|
image = new BufferedImage(256, 256, BufferedImage.TYPE_INT_RGB);
|
||||||
|
for (int x = 0; x < 256; x ++) {
|
||||||
|
for (int z = 0; z < 256; z ++) {
|
||||||
|
int temp = 0xff * temperatureMap[x][z] / 100;
|
||||||
|
temp = temp << 16;
|
||||||
|
image.setRGB(x, z, temp);
|
||||||
|
subImage.setRGB(x, z, (0xff * (int) (temperatureMap[x][z] / 20) / 5) << 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImageIO.write(image, "png", new File("out/" + region.getX() + "." +region.getZ() + "/temperatureMap.png"));
|
||||||
|
ImageIO.write(subImage, "png", new File("out/" + region.getX() + "." +region.getZ() + "/reg_temperatureMap.png"));
|
||||||
|
image = new BufferedImage(256, 256, BufferedImage.TYPE_INT_RGB);
|
||||||
|
for (int x = 0; x < 256; x ++) {
|
||||||
|
for (int z = 0; z < 256; z ++) {
|
||||||
|
int wet = 0xff * wetMap[x][z] / 100;
|
||||||
|
image.setRGB(x, z, wet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImageIO.write(image, "png", new File("out/" + region.getX() + "." +region.getZ() + "/wetMap.png"));
|
||||||
|
image = new BufferedImage(256, 256, BufferedImage.TYPE_INT_RGB);
|
||||||
|
for (int x = 0; x < 256; x ++) {
|
||||||
|
for (int z = 0; z < 256; z ++) {
|
||||||
|
image.setRGB(x, z, biomes[x][z].getColor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImageIO.write(image, "png", new File("out/" + region.getX() + "." +region.getZ() + "/biomeMap.png"));
|
||||||
|
} catch (Exception e) {}
|
||||||
|
// ================================ DEBUG FINISH =======================================
|
||||||
|
|
||||||
|
for (int x = 0; x < size; x ++) {
|
||||||
|
for (int z = 0; z < size; z ++) {
|
||||||
|
region.setBiome(x, z, biomes[x][z]);
|
||||||
|
if (heightMap[x][z] < WorldConstants.WORLD_SEA_LEVEL) {
|
||||||
|
for (int y = 0; y < WorldConstants.WORLD_SEA_LEVEL; y ++) {
|
||||||
|
Chunk chunk = region.getChunkAt(x / 16, y / 16, z / 16);
|
||||||
|
if (y == 0) {
|
||||||
|
chunk.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.BEDROCK, 0));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (y < heightMap[x][z]) {
|
||||||
|
if (y < heightMap[x][z] - grassMap[x][z]) {
|
||||||
|
chunk.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.STONE, 0));
|
||||||
|
} else {
|
||||||
|
chunk.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.SAND, 0));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chunk.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.WATER, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int y = 0; y < heightMap[x][z]; y++) {
|
||||||
|
Chunk chunk = region.getChunkAt(x / 16, y / 16, z / 16);
|
||||||
|
if (y == 0) {
|
||||||
|
chunk.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.BEDROCK, 0));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (y < heightMap[x][z] - grassMap[x][z]) {
|
||||||
|
chunk.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.STONE, 0));
|
||||||
|
} else {
|
||||||
|
if (biomes[x][z] == Biome.DESERT || biomes[x][z] == Biome.DESERT_HILLS) {
|
||||||
|
chunk.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.SAND, 0));
|
||||||
|
} else if (biomes[x][z] == Biome.TAIGA || biomes[x][z] == Biome.TAIGA_HILLS) {
|
||||||
|
chunk.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.DIRT, 0));
|
||||||
|
} else {
|
||||||
|
chunk.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.GRASS, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Biome selectBiome (Temperature temperature, Wetness wetness, int height) {
|
||||||
|
if (temperature == Temperature.FROST) {
|
||||||
|
if (height < WorldConstants.WORLD_SEA_LEVEL) {
|
||||||
|
return Biome.FROZEN_OCEAN;
|
||||||
|
} else {
|
||||||
|
if (height > (WorldConstants.WORLD_SEA_LEVEL + WorldConstants.WORLD_MAX_HEIGHT) / 2) {
|
||||||
|
return Biome.ICE_MOUNTAINS;
|
||||||
|
} else {
|
||||||
|
return Biome.ICE_PLAINS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (height < WorldConstants.WORLD_SEA_LEVEL) {
|
||||||
|
if (height < (WorldConstants.WORLD_SEA_LEVEL + WorldConstants.WORLD_MIN_HEIGHT) / 2){
|
||||||
|
return Biome.DEEP_OCEAN;
|
||||||
|
} else {
|
||||||
|
return Biome.OCEAN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (temperature == Temperature.COLD) {
|
||||||
|
if (height > (WorldConstants.WORLD_SEA_LEVEL + WorldConstants.WORLD_MAX_HEIGHT) / 2) {
|
||||||
|
return Biome.TAIGA_HILLS;
|
||||||
|
} else {
|
||||||
|
return Biome.TAIGA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (temperature == Temperature.WARM) {
|
||||||
|
if (wetness.ordinal() < 2) {
|
||||||
|
return Biome.PLAINS;
|
||||||
|
} else if (wetness == Wetness.WATER){
|
||||||
|
return Biome.SWAMPLAND;
|
||||||
|
} else {
|
||||||
|
if (height > (WorldConstants.WORLD_SEA_LEVEL + WorldConstants.WORLD_MAX_HEIGHT) / 2) {
|
||||||
|
return Biome.FOREST_HILLS;
|
||||||
|
} else {
|
||||||
|
return Biome.FOREST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (temperature == Temperature.HOTTEST && wetness.ordinal() < 2) {
|
||||||
|
if (height > (WorldConstants.WORLD_SEA_LEVEL + WorldConstants.WORLD_MAX_HEIGHT) / 2) {
|
||||||
|
return Biome.DESERT_HILLS;
|
||||||
|
} else {
|
||||||
|
return Biome.DESERT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wetness.ordinal() > 2) {
|
||||||
|
if (height > (WorldConstants.WORLD_SEA_LEVEL + WorldConstants.WORLD_MAX_HEIGHT) / 2) {
|
||||||
|
return Biome.JUNGLE_HILLS;
|
||||||
|
} else {
|
||||||
|
return Biome.JUNGLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return Biome.PLAINS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void smooth (int [][] map) {
|
||||||
|
final int[][] original = map.clone();
|
||||||
|
for (int y = 1; y < map.length - 1; y ++) {
|
||||||
|
for (int x = 1; x < map[0].length - 1; x ++) {
|
||||||
|
int mid = 0;
|
||||||
|
for (int tx = x - 1; tx <= x + 1; tx ++) {
|
||||||
|
for (int ty = y - 1; ty <= y + 1; ty ++) {
|
||||||
|
mid += original[tx][ty];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map[x][y] = mid / 9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
private class NoiseGenerator {
|
||||||
|
int size = 256;
|
||||||
|
int mask = size - 1;
|
||||||
|
int[] perm = new int[size];
|
||||||
|
double[] gradsX = new double[size];
|
||||||
|
double[] gradsY = new double[size];
|
||||||
|
private final int seed;
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
int other = rand(i) % (i + 1);
|
||||||
|
if (i > other)
|
||||||
|
perm[i] = perm[other];
|
||||||
|
perm[other] = i;
|
||||||
|
gradsX[i] = Math.cos(2.0f * Math.PI * i / size);
|
||||||
|
gradsY[i] = Math.sin(2.0f * Math.PI * i / size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double f(double t) {
|
||||||
|
t = Math.abs(t);
|
||||||
|
return t >= 1.0f ? 0.0f : 1.0f -
|
||||||
|
(3.0f - 2.0f * t) * t * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
double surflet(double x, double y, double gradX, double gradY) {
|
||||||
|
return f(x) * f(y) * (gradX * x + gradY * y);
|
||||||
|
}
|
||||||
|
|
||||||
|
double noise(double x, double y) {
|
||||||
|
float result = 0.0f;
|
||||||
|
int cellX = (int)(x);
|
||||||
|
int cellY = (int)(y);
|
||||||
|
for (int gridY = cellY; gridY <= cellY + 1; ++gridY)
|
||||||
|
for (int gridX = cellX; gridX <= cellX + 1; ++gridX) {
|
||||||
|
int hash = perm[(perm[gridX & mask] + gridY) & mask];
|
||||||
|
result += surflet(x - gridX, y - gridY,
|
||||||
|
gradsX[hash], gradsY[hash]);
|
||||||
|
}
|
||||||
|
return (result + 1) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rand(int i) {
|
||||||
|
int x = (i * i) % 256;
|
||||||
|
int y = (i + i * x) % 256;
|
||||||
|
return (int) (Integer.MAX_VALUE * SeedRandomGenerator.random(x, y, seed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ public final class SeedRandomGenerator {
|
|||||||
public static double random (int x, int y, int seed) {
|
public static double random (int x, int y, int seed) {
|
||||||
x = Math.abs(x - y) + 1;
|
x = Math.abs(x - y) + 1;
|
||||||
y = Math.abs(y - x) + 1;
|
y = Math.abs(y - x) + 1;
|
||||||
for (int i = 0; i < 40; i ++) {
|
for (int i = 0; i < 20; i ++) {
|
||||||
int a1 = x % 13;
|
int a1 = x % 13;
|
||||||
int a2 = x % 31;
|
int a2 = x % 31;
|
||||||
int a3 = x % 89;
|
int a3 = x % 89;
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package mc.world.generated_world;
|
||||||
|
|
||||||
|
public final class WorldConstants {
|
||||||
|
|
||||||
|
public static final String CHUNK_FILE_NAME_TEMPLATE = "chunk_{0}_{1}_{2}.dat";
|
||||||
|
public static final String BIOME_FILE_NAME_TEMPLATE = "biome_{0}_{1}.dat";
|
||||||
|
public static final String REGION_FILE_NAME_TEMPLATE = "r.{0}.{1}";
|
||||||
|
|
||||||
|
public static final int WORLD_MIN_HEIGHT = 28;
|
||||||
|
public static final int WORLD_SEA_LEVEL = 64;
|
||||||
|
public static final int WORLD_MAX_HEIGHT = 128;
|
||||||
|
|
||||||
|
public static final int LANDFILL_GRASS_SURFACE_THIN = 5;
|
||||||
|
public static final double WORLD_ROUGHNRESS = 0.35;
|
||||||
|
|
||||||
|
private WorldConstants () {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
package mc.world.generated_world.chunk;
|
||||||
|
|
||||||
|
import mc.core.block.Block;
|
||||||
|
import mc.core.world.Biome;
|
||||||
|
import mc.core.world.Chunk;
|
||||||
|
|
||||||
|
public class ChunkProxy implements Chunk {
|
||||||
|
private final Chunk chunk;
|
||||||
|
private volatile long lastUsage = System.currentTimeMillis();
|
||||||
|
|
||||||
|
public ChunkProxy(Chunk chunk) {
|
||||||
|
this.chunk = chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLastUsage() {
|
||||||
|
synchronized (chunk) {
|
||||||
|
return lastUsage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void use () {
|
||||||
|
synchronized (chunk) {
|
||||||
|
lastUsage = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBlockType(int x, int y, int z) {
|
||||||
|
use();
|
||||||
|
return chunk.getBlockType(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBlockType(int x, int y, int z, int type) {
|
||||||
|
use();
|
||||||
|
chunk.setBlockType(x, y, z, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBlockMetadata(int x, int y, int z) {
|
||||||
|
use();
|
||||||
|
return chunk.getBlockMetadata(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBlockMetadata(int x, int y, int z, int metadata) {
|
||||||
|
use();
|
||||||
|
chunk.setBlockMetadata(x, y, z, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBlockLight(int x, int y, int z) {
|
||||||
|
use();
|
||||||
|
return chunk.getBlockLight(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBlockLight(int x, int y, int z, int lightLevel) {
|
||||||
|
use();
|
||||||
|
chunk.setBlockLight(x, y, z, lightLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSkyLight(int x, int y, int z) {
|
||||||
|
use();
|
||||||
|
return chunk.getSkyLight(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSkyLight(int x, int y, int z, int lightLevel) {
|
||||||
|
use();
|
||||||
|
chunk.setSkyLight(x, y, z, lightLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAddition(int x, int y, int z) {
|
||||||
|
use();
|
||||||
|
return chunk.getAddition(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAddition(int x, int y, int z, int value) {
|
||||||
|
use();
|
||||||
|
chunk.setAddition(x, y, z, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Biome getBiome(int x, int z) {
|
||||||
|
use();
|
||||||
|
return chunk.getBiome(x, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBiome(int x, int z, Biome biome) {
|
||||||
|
use();
|
||||||
|
chunk.setBiome(x, z, biome);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getX() {
|
||||||
|
use();
|
||||||
|
return chunk.getX();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getY() {
|
||||||
|
use();
|
||||||
|
return chunk.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getZ() {
|
||||||
|
use();
|
||||||
|
return chunk.getZ();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Block[] getNotAirBlocks() {
|
||||||
|
use();
|
||||||
|
return chunk.getNotAirBlocks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBlock(int x, int y, int z, Block block) {
|
||||||
|
use();
|
||||||
|
chunk.setBlock(x, y, z, block);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package mc.world.generated_world.chunk;
|
||||||
|
|
||||||
|
import mc.core.world.Chunk;
|
||||||
|
import mc.core.world.ChunkLoader;
|
||||||
|
|
||||||
|
public interface ProxiedChunkLoader extends ChunkLoader {
|
||||||
|
@Override
|
||||||
|
Chunk loadOrGenerateChunk(int x, int y, int z);
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package mc.world.generated_world.region;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import mc.core.world.*;
|
||||||
|
import mc.world.generated_world.chunk.ChunkProxy;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class RegionImpl implements Region{
|
||||||
|
@Getter
|
||||||
|
private final int x;
|
||||||
|
@Getter
|
||||||
|
private final int z;
|
||||||
|
private final ChunkProxy[][][] chunks = new ChunkProxy[16][16][16];
|
||||||
|
private final Biome[][] biomes = new Biome[16][16];
|
||||||
|
@Getter@Setter
|
||||||
|
private transient World world;
|
||||||
|
@Autowired
|
||||||
|
private ChunkLoader chunkLoader;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Chunk getChunkAt(int x, int y, int z) {
|
||||||
|
if (x < 0 || y < 0 || z < 0 || x >= 16 || y >= 16 || z >= 16) {
|
||||||
|
throw new RuntimeException(MessageFormat.format("Invalid chunk coordinates [{0} {1} {2}]", x, y, z));
|
||||||
|
}
|
||||||
|
Chunk chunk = chunks[x][y][z];
|
||||||
|
if (chunk == null) {
|
||||||
|
chunk = chunkLoader.loadOrGenerateChunk(x, y, z);
|
||||||
|
chunks[x][y][z] = new ChunkProxy(chunk);
|
||||||
|
}
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setChunk(int x, int y, int z, Chunk chunk) {
|
||||||
|
if (x < 0 || y < 0 || z < 0 || x >= 16 || y >= 16 || z >= 16) {
|
||||||
|
throw new RuntimeException(MessageFormat.format("Invalid chunk coordinates [{0} {1} {2}]", x, y, z));
|
||||||
|
}
|
||||||
|
chunks[x][y][z] = new ChunkProxy(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Biome getBiomeAt(int x, int z) {
|
||||||
|
if (x < 0 || z < 0 || x >= 16 || z >= 16) {
|
||||||
|
throw new RuntimeException(MessageFormat.format("Invalid biome coordinates [{0} {1}]", x, z));
|
||||||
|
}
|
||||||
|
return biomes[x][z];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBiome(int x, int z, Biome biome) {
|
||||||
|
if (x < 0 || z < 0 || x >= 16 || z >= 16) {
|
||||||
|
throw new RuntimeException(MessageFormat.format("Invalid biome coordinates [{0} {1}]", x, z));
|
||||||
|
}
|
||||||
|
biomes[x][z] = biome;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package mc.world.generated_world.word;
|
||||||
|
|
||||||
|
public enum Temperature {
|
||||||
|
FROST,
|
||||||
|
COLD,
|
||||||
|
WARM,
|
||||||
|
HOT,
|
||||||
|
HOTTEST
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package mc.world.generated_world.word;
|
||||||
|
|
||||||
|
public enum Wetness {
|
||||||
|
DRYEST,
|
||||||
|
DRY,
|
||||||
|
WET,
|
||||||
|
WETTER,
|
||||||
|
WATER
|
||||||
|
}
|
||||||
@@ -9,6 +9,38 @@ import java.io.File;
|
|||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class SeedRandomGeneratorTest {
|
public class SeedRandomGeneratorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void randomGenSpeed () {
|
||||||
|
SeedRandomGenerator.random(0, 0, 0);
|
||||||
|
long avg = 0;
|
||||||
|
long min = -1;
|
||||||
|
long max = 0;
|
||||||
|
for (int i = 0; i < 500; i ++) {
|
||||||
|
int x = (int) (Math.random() * 10000);
|
||||||
|
int y = (int) (Math.random() * 10000);
|
||||||
|
int seed = (int) (Math.random() * 10000);
|
||||||
|
long time = System.nanoTime();
|
||||||
|
SeedRandomGenerator.random(x, y, seed);
|
||||||
|
time = System.nanoTime() - time;
|
||||||
|
System.out.printf("[%s] \t%.3fms\n", i+1, time/1000d);
|
||||||
|
avg += time;
|
||||||
|
if (min == -1) {
|
||||||
|
min = time;
|
||||||
|
} else if (min > time) {
|
||||||
|
min = time;
|
||||||
|
}
|
||||||
|
if (max < time) {
|
||||||
|
max = time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
System.out.printf("Average time: %.3fms\n", avg/500000d);
|
||||||
|
System.out.printf("Minimum time: %.3fms\n", min/1000d);
|
||||||
|
System.out.printf("Maximum time: %.3fms\n", max/1000d);
|
||||||
|
assertTrue(avg/500 < 5000);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void randomTest() throws Exception {
|
public void randomTest() throws Exception {
|
||||||
double maxDiff = 0;
|
double maxDiff = 0;
|
||||||
@@ -33,10 +65,12 @@ public class SeedRandomGeneratorTest {
|
|||||||
maxDisp = disp;
|
maxDisp = disp;
|
||||||
}
|
}
|
||||||
System.out.printf("Iteration %d.\t mid: %.3f, \tdisp %.6f\n", i + 1, mid, disp);
|
System.out.printf("Iteration %d.\t mid: %.3f, \tdisp %.6f\n", i + 1, mid, disp);
|
||||||
|
assertTrue(Math.abs(mid - 0.5) < 0.15);
|
||||||
}
|
}
|
||||||
System.out.printf("Max diff: %.3f\n", maxDiff);
|
System.out.printf("Max diff: %.3f\n", maxDiff);
|
||||||
System.out.printf("Max disp: %.6f\n", maxDisp);
|
System.out.printf("Max disp: %.6f\n", maxDisp);
|
||||||
|
|
||||||
|
|
||||||
assertTrue(maxDiff > 0);
|
assertTrue(maxDiff > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user