Added OpenSimplex2 and a fancy world generator
This commit is contained in:
@@ -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
|
||||
|
@@ -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;
|
||||
//
|
||||
|
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@@ -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")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user