Added OpenSimplex2 and a fancy world generator

This commit is contained in:
2021-01-02 20:48:29 +03:00
parent ea56b642ee
commit cc031529a1
6 changed files with 1187 additions and 37 deletions

View File

@@ -30,7 +30,7 @@ public class PlayerManager {
this.players.add(player);
}
private static final Vec3i SPAWN = new Vec3i(8, 8, 20);
private static final Vec3i SPAWN = new Vec3i(8, 8, 900);
public EntityData conjurePlayerEntity(String login) {
// TODO Live up to the name

View File

@@ -21,6 +21,8 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Locale;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.client.Client;
import ru.windcorp.progressia.client.ClientState;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.font.Font;
@@ -81,6 +83,12 @@ public class LayerTestGUI extends GUILayer {
128
));
panel.addChild(new DynamicLabel(
"PosDisplay", new Font().withColor(0x37A3E6).deriveShadow(),
LayerTestGUI::getPos,
128
));
panel.getChildren().forEach(c -> {
if (c instanceof Label) {
labels.add((Label) c);
@@ -151,6 +159,14 @@ public class LayerTestGUI extends GUILayer {
return String.format(Locale.US, "TPS: %5.1f", TPS_RECORD.update(server.getTPS()));
}
private static String getPos() {
Client client = ClientState.getInstance();
if (client == null) return "Pos: n/a";
Vec3 pos = client.getCamera().getLastAnchorPosition();
return String.format(Locale.US, "Pos: %+7.1f %+7.1f %+7.1f", pos.x, pos.y, pos.z);
}
// private static class DebugComponent extends Component {
// private final int color;
//

View File

@@ -0,0 +1,126 @@
package ru.windcorp.progressia.test.gen;
import kdotjpg.opensimplex2.areagen.OpenSimplex2S;
import ru.windcorp.progressia.server.world.WorldLogic;
class TestTerrainGenerator {
@FunctionalInterface
private interface Func2D {
double compute(double x, double y);
}
private final OpenSimplex2S noise;
private final Func2D shape;
public TestTerrainGenerator(TestWorldGenerator testWorldGenerator, WorldLogic world) {
this.noise = new OpenSimplex2S("We're getting somewhere".hashCode());
Func2D plainsHeight =
tweak(
octaves(
tweak(primitive(), 0.01, 0.5),
2, 3
),
1, 0.2, 0.2
);
Func2D mountainsHeight =
tweak(
octaves(
ridge(tweak(primitive(), 0.01, 1)),
2, 1.5, 12
),
1, 3
);
Func2D mountainousity =
tweak(
octaves(
tweak(primitive(), 0.007, 1),
2, 3
),
1, 1, -0.25
);
shape = tweak(
add(multiply(squash(mountainousity, 10), mountainsHeight), plainsHeight),
0.001, 1000, 0
);
}
public void compute(int startX, int startY, double[][] heightMap, double[][] slopeMap) {
for (int x = 0; x < heightMap.length; ++x) {
for (int y = 0; y < heightMap.length; ++y) {
heightMap[x][y] = shape.compute(x + startX, y + startY);
slopeMap[x][y] = computeSlope(shape, x + startX, y + startY, heightMap[x][y]);
}
}
}
private double computeSlope(Func2D f, double x0, double y0, double f0) {
double di = 0.5;
double dfdx = (f.compute(x0 + di, y0) - f0) / di;
double dfdy = (f.compute(x0, y0 + di) - f0) / di;
return Math.hypot(dfdx, dfdy);
}
/*
* Utility functions
*/
private Func2D primitive() {
return noise::noise2;
}
private Func2D add(Func2D a, Func2D b) {
return (x, y) -> a.compute(x, y) + b.compute(x, y);
}
private Func2D multiply(Func2D a, Func2D b) {
return (x, y) -> a.compute(x, y) * b.compute(x, y);
}
private Func2D tweak(Func2D f, double scale, double amplitude, double bias) {
return (x, y) -> f.compute(x * scale, y * scale) * amplitude + bias;
}
private Func2D tweak(Func2D f, double scale, double amplitude) {
return tweak(f, scale, amplitude, 0);
}
private Func2D octaves(Func2D f, double scaleFactor, double amplitudeFactor, int octaves) {
return (x, y) -> {
double result = 0;
double scale = 1;
double amplitude = 1;
for (int i = 0; i < octaves; ++i) {
result += f.compute(x * scale, y * scale) * amplitude;
scale *= scaleFactor;
amplitude /= amplitudeFactor;
}
return result;
};
}
private Func2D octaves(Func2D f, double factor, int octaves) {
return octaves(f, factor, factor, octaves);
}
private Func2D squash(Func2D f, double slope) {
return (x, y) -> 1 / (1 + Math.exp(-slope * f.compute(x, y)));
}
private Func2D ridge(Func2D f) {
return (x, y) -> {
double result = 1 - Math.abs(f.compute(x, y));
return result * result;
};
}
}

View File

@@ -23,8 +23,11 @@ import ru.windcorp.progressia.server.world.generation.AbstractWorldGenerator;
public class TestWorldGenerator extends AbstractWorldGenerator<Boolean> {
private final TestTerrainGenerator terrainGen;
public TestWorldGenerator(WorldLogic world) {
super("Test:WorldGenerator", Boolean.class);
this.terrainGen = new TestTerrainGenerator(this, world);
world.getData().addListener(new WorldDataListener() {
@Override
@@ -66,28 +69,26 @@ public class TestWorldGenerator extends AbstractWorldGenerator<Boolean> {
BlockData stone = BlockDataRegistry.getInstance().get("Test:Stone");
BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
final float maxHeight = 32;
final float rho = 2000;
double[][] heightMap = new double[bpc][bpc];
double[][] gradMap = new double[bpc][bpc];
int[][] heightMap = new int[bpc][bpc];
int startX = Coordinates.getInWorld(chunk.getX(), 0);
int startY = Coordinates.getInWorld(chunk.getY(), 0);
int startZ = Coordinates.getInWorld(chunk.getZ(), 0);
for (int yic = 0; yic < heightMap.length; ++yic) {
int yiw = Coordinates.getInWorld(chunk.getY(), yic);
for (int xic = 0; xic < heightMap[yic].length; ++xic) {
int xiw = Coordinates.getInWorld(chunk.getX(), xic);
int rsq = (xiw*xiw + yiw*yiw);
heightMap[xic][yic] = (int) (rsq / (rho + rsq) * maxHeight) - chunk.getZ()*bpc;
}
}
terrainGen.compute(startX, startY, heightMap, gradMap);
VectorUtil.iterateCuboid(0, 0, 0, bpc, bpc, bpc, pos -> {
int layer = pos.z - heightMap[pos.x][pos.y];
double layer = pos.z - heightMap[pos.x][pos.y] + startZ;
if (layer < -4) {
chunk.setBlock(pos, stone, false);
} else if (layer < 0) {
chunk.setBlock(pos, dirt, false);
if (gradMap[pos.x][pos.y] > 0.3) {
chunk.setBlock(pos, stone, false);
} else {
chunk.setBlock(pos, dirt, false);
}
} else {
chunk.setBlock(pos, air, false);
}
@@ -140,6 +141,7 @@ public class TestWorldGenerator extends AbstractWorldGenerator<Boolean> {
assert chunk != null : "Something went wrong when populating chunk at (" + chunkPos.x + "; " + chunkPos.y + "; " + chunkPos.z + ")";
BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
BlockData dirt = BlockDataRegistry.getInstance().get("Test:Dirt");
Vec3i biw = new Vec3i();
@@ -159,7 +161,7 @@ public class TestWorldGenerator extends AbstractWorldGenerator<Boolean> {
if (biw.z == maxZ) continue;
if (biw.z < minZ) continue;
addTiles(chunk, biw, world, random);
addTiles(chunk, biw, world, random, world.getBlock(biw) == dirt);
}
}
@@ -167,9 +169,9 @@ public class TestWorldGenerator extends AbstractWorldGenerator<Boolean> {
chunk.setGenerationHint(true);
}
private void addTiles(ChunkData chunk, Vec3i biw, WorldData world, Random random) {
addGrass(chunk, biw, world, random);
addDecor(chunk, biw, world, random);
private void addTiles(ChunkData chunk, Vec3i biw, WorldData world, Random random, boolean isDirt) {
if (isDirt) addGrass(chunk, biw, world, random);
addDecor(chunk, biw, world, random, isDirt);
}
private void addGrass(ChunkData chunk, Vec3i biw, WorldData world, Random random) {
@@ -191,23 +193,31 @@ public class TestWorldGenerator extends AbstractWorldGenerator<Boolean> {
}
}
private void addDecor(ChunkData chunk, Vec3i biw, WorldData world, Random random) {
if (random.nextInt(8) == 0) {
world.getTiles(biw, BlockFace.TOP).addFarthest(
TileDataRegistry.getInstance().get("Test:Sand")
);
}
if (random.nextInt(8) == 0) {
world.getTiles(biw, BlockFace.TOP).addFarthest(
TileDataRegistry.getInstance().get("Test:Stones")
);
}
if (random.nextInt(8) == 0) {
world.getTiles(biw, BlockFace.TOP).addFarthest(
TileDataRegistry.getInstance().get("Test:YellowFlowers")
);
private void addDecor(ChunkData chunk, Vec3i biw, WorldData world, Random random, boolean isDirt) {
if (isDirt) {
if (random.nextInt(8) == 0) {
world.getTiles(biw, BlockFace.TOP).addFarthest(
TileDataRegistry.getInstance().get("Test:Sand")
);
}
if (random.nextInt(8) == 0) {
world.getTiles(biw, BlockFace.TOP).addFarthest(
TileDataRegistry.getInstance().get("Test:Stones")
);
}
if (random.nextInt(8) == 0) {
world.getTiles(biw, BlockFace.TOP).addFarthest(
TileDataRegistry.getInstance().get("Test:YellowFlowers")
);
}
} else {
if (random.nextInt(2) == 0) {
world.getTiles(biw, BlockFace.TOP).addFarthest(
TileDataRegistry.getInstance().get("Test:Stones")
);
}
}
}