diff --git a/src/main/java/ru/windcorp/progressia/common/util/ArrayFloatRangeMap.java b/src/main/java/ru/windcorp/progressia/common/util/ArrayFloatRangeMap.java
new file mode 100644
index 0000000..4eea3f6
--- /dev/null
+++ b/src/main/java/ru/windcorp/progressia/common/util/ArrayFloatRangeMap.java
@@ -0,0 +1,225 @@
+/*
+ * Progressia
+ * Copyright (C) 2020-2021 Wind Corporation and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package ru.windcorp.progressia.common.util;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Objects;
+
+public class ArrayFloatRangeMap implements FloatRangeMap {
+
+ protected static class Node implements Comparable> {
+ public float pos;
+ public E value;
+
+ public Node(float pos, E value) {
+ this.pos = pos;
+ this.value = value;
+ }
+
+ @Override
+ public int compareTo(Node o) {
+ return Float.compare(pos, o.pos);
+ }
+ }
+
+ /*
+ * Expects a random-access list
+ */
+ protected final List> nodes;
+ protected int ranges = 0;
+
+ protected static final int DEFAULT_CAPACITY = 16;
+
+ public ArrayFloatRangeMap(int capacity) {
+ this.nodes = new ArrayList<>(2 * capacity);
+ }
+
+ public ArrayFloatRangeMap() {
+ this(DEFAULT_CAPACITY);
+ }
+
+ @Override
+ public int size() {
+ return this.ranges;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return new Iterator() {
+
+ private int nextIndex = 0;
+
+ {
+ assert nodes.isEmpty() || nodes.get(nextIndex).value != null;
+ }
+
+ private void findNext() {
+ while (nextIndex < nodes.size()) {
+ nextIndex++;
+ Node node = nodes.get(nextIndex);
+ if (node.value != null) return;
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return nextIndex < nodes.size();
+ }
+
+ @Override
+ public E next() {
+ E result = nodes.get(nextIndex).value;
+ findNext();
+ return result;
+ }
+ };
+ }
+
+ /**
+ * Returns an index of the smallest {@link Node} larger than or exactly at
+ * {@code position}.
+ *
+ * @param position the position to look up
+ * @return an index in the {@link #nodes} list containing the first
+ * {@link Node} whose {@link Node#pos} is not smaller than
+ * {@code position}, or {@code nodes.size()} if no such index exists
+ */
+ protected int findCeiling(float position) {
+
+ /*
+ * Implementation based on OpenJDK's
+ * Collections.indexedBinarySearch(List, Comparator)
+ */
+
+ int low = 0;
+ int high = nodes.size() - 1;
+
+ while (low <= high) {
+ int mid = (low + high) >>> 1;
+ float midVal = nodes.get(mid).pos;
+ int cmp = Float.compare(midVal, position);
+
+ if (cmp < 0)
+ low = mid + 1;
+ else if (cmp > 0)
+ high = mid - 1;
+ else
+ return mid; // key found
+ }
+
+ return low; // the insertion point is the desired index
+ }
+
+ /**
+ * Returns an index of the largest {@link Node} smaller than or exactly at
+ * {@code position}.
+ *
+ * @param position the position to look up
+ * @return an index in the {@link #nodes} list containing the last
+ * {@link Node} whose {@link Node#pos} is not greater than
+ * {@code position}, or {@code -1} if no such index exists
+ */
+ protected int findFloor(float position) {
+
+ /*
+ * Implementation based on OpenJDK's
+ * Collections.indexedBinarySearch(List, Comparator)
+ */
+
+ int low = 0;
+ int high = nodes.size() - 1;
+
+ while (low <= high) {
+ int mid = (low + high) >>> 1;
+ float midVal = nodes.get(mid).pos;
+ int cmp = Float.compare(midVal, position);
+
+ if (cmp < 0)
+ low = mid + 1;
+ else if (cmp > 0)
+ high = mid - 1;
+ else
+ return mid; // key found
+ }
+
+ return low - 1; // the insertion point immediately follows the desired index
+ }
+
+ protected Node getEffectiveNode(float at) {
+ int effectiveNodeIndex = findFloor(at);
+ if (effectiveNodeIndex < 0) return null;
+ return nodes.get(effectiveNodeIndex);
+ }
+
+ @Override
+ public E get(float at) {
+ Node effectiveNode = getEffectiveNode(at);
+ return effectiveNode == null ? null : effectiveNode.value;
+ }
+
+ @Override
+ public void put(float min, float max, E element) {
+ Objects.requireNonNull(element, "element");
+
+ if (!(max > min)) // This funky construction also deals with NaNs since NaNs always fail any comparison
+ {
+ throw new IllegalArgumentException(max + " is not greater than " + min);
+ }
+
+ int indexOfInsertionOfMin = findCeiling(min);
+
+ nodes.add(indexOfInsertionOfMin, new Node(min, element));
+ ranges++;
+
+ ListIterator> it = nodes.listIterator(indexOfInsertionOfMin + 1);
+ E elementEffectiveImmediatelyAfterInsertedRange = null;
+
+ if (indexOfInsertionOfMin > 0) {
+ elementEffectiveImmediatelyAfterInsertedRange = nodes.get(indexOfInsertionOfMin - 1).value;
+ }
+
+ while (it.hasNext()) {
+ Node node = it.next();
+
+ if (node.pos >= max) {
+ break;
+ }
+
+ elementEffectiveImmediatelyAfterInsertedRange = node.value;
+ if (elementEffectiveImmediatelyAfterInsertedRange != null) {
+ // Removing an actual range
+ ranges--;
+ }
+ it.remove();
+ }
+
+ if (max != Float.POSITIVE_INFINITY) {
+ nodes.add(indexOfInsertionOfMin + 1, new Node(max, elementEffectiveImmediatelyAfterInsertedRange));
+
+ if (elementEffectiveImmediatelyAfterInsertedRange != null) {
+ // We might have added one right back
+ ranges++;
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/progressia/common/util/FloatRangeMap.java b/src/main/java/ru/windcorp/progressia/common/util/FloatRangeMap.java
new file mode 100644
index 0000000..303d6a8
--- /dev/null
+++ b/src/main/java/ru/windcorp/progressia/common/util/FloatRangeMap.java
@@ -0,0 +1,36 @@
+/*
+ * Progressia
+ * Copyright (C) 2020-2021 Wind Corporation and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package ru.windcorp.progressia.common.util;
+
+public interface FloatRangeMap extends Iterable {
+
+ void put(float min, float max, E element);
+ E get(float at);
+
+ int size();
+
+ default boolean defines(float position) {
+ return get(position) != null;
+ }
+
+ default E getOrDefault(float at, E def) {
+ E result = get(at);
+ return result == null ? def : result;
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java b/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java
index 6bef350..f25368f 100644
--- a/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java
+++ b/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java
@@ -290,6 +290,88 @@ public class VectorUtil {
);
return output;
}
+
+ public static Vec3i sort(Vec3i input, Vec3i output) {
+ if (output == null) {
+ output = new Vec3i();
+ }
+
+ int ax = input.x, ay = input.y, az = input.z;
+
+ if (ax > ay) {
+ if (ax > az) {
+ output.x = ax;
+ output.y = ay > az ? ay : az;
+ output.z = ay > az ? az : ay;
+ } else {
+ output.x = az;
+ output.y = ax;
+ output.z = ay;
+ }
+ } else {
+ if (ay > az) {
+ output.x = ay;
+ output.y = ax > az ? ax : az;
+ output.z = ax > az ? az : ax;
+ } else {
+ output.x = az;
+ output.y = ay;
+ output.z = ax;
+ }
+ }
+
+ return output;
+ }
+
+ public static Vec3 sort(Vec3 input, Vec3 output) {
+ if (output == null) {
+ output = new Vec3();
+ }
+
+ float ax = input.x, ay = input.y, az = input.z;
+
+ if (ax > ay) {
+ if (ax > az) {
+ output.x = ax;
+ output.y = ay > az ? ay : az;
+ output.z = ay > az ? az : ay;
+ } else {
+ output.x = az;
+ output.y = ax;
+ output.z = ay;
+ }
+ } else {
+ if (ay > az) {
+ output.x = ay;
+ output.y = ax > az ? ax : az;
+ output.z = ax > az ? az : ax;
+ } else {
+ output.x = az;
+ output.y = ay;
+ output.z = ax;
+ }
+ }
+
+ return output;
+ }
+
+ public static Vec3i sortAfterAbs(Vec3i input, Vec3i output) {
+ if (output == null) {
+ output = new Vec3i();
+ }
+
+ input.abs(output);
+ return sort(output, output);
+ }
+
+ public static Vec3 sortAfterAbs(Vec3 input, Vec3 output) {
+ if (output == null) {
+ output = new Vec3();
+ }
+
+ input.abs(output);
+ return sort(output, output);
+ }
public static float get(Vec2 v, Axis a) {
switch (a) {
diff --git a/src/main/java/ru/windcorp/progressia/common/world/BlockRay.java b/src/main/java/ru/windcorp/progressia/common/world/BlockRay.java
index 9f35d4c..d7dffc1 100644
--- a/src/main/java/ru/windcorp/progressia/common/world/BlockRay.java
+++ b/src/main/java/ru/windcorp/progressia/common/world/BlockRay.java
@@ -90,11 +90,7 @@ public class BlockRay {
VectorUtil.set(block, axis, VectorUtil.get(block, axis) + (int) signum(VectorUtil.get(direction, axis)));
// position += direction * tMin
- VectorUtil.linearCombination(position, 1, direction, tMin, position); // position
- // +=
- // direction
- // *
- // tMin
+ VectorUtil.linearCombination(position, 1, direction, tMin, position);
distance += tMin;
// position.(axis) = round(position.(axis))
diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/GenericChunk.java b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericChunk.java
index 95c18be..fa80269 100644
--- a/src/main/java/ru/windcorp/progressia/common/world/generic/GenericChunk.java
+++ b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericChunk.java
@@ -20,6 +20,7 @@ package ru.windcorp.progressia.common.world.generic;
import java.util.function.Consumer;
+import glm.Glm;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.util.VectorUtil;
import ru.windcorp.progressia.common.util.Vectors;
@@ -118,6 +119,64 @@ public interface GenericChunk, B exten
default int getMaxZ() {
return Coordinates.getInWorld(getZ(), BLOCKS_PER_CHUNK - 1);
}
+
+ default Vec3i getMinBIW(Vec3i output) {
+ if (output == null) {
+ output = new Vec3i();
+ }
+
+ output.set(getMinX(), getMinY(), getMinZ());
+
+ return output;
+ }
+
+ default Vec3i getMaxBIW(Vec3i output) {
+ if (output == null) {
+ output = new Vec3i();
+ }
+
+ output.set(getMaxX(), getMaxY(), getMaxZ());
+
+ return output;
+ }
+
+ default Vec3i getMinBIWRel(Vec3i output) {
+ if (output == null) {
+ output = new Vec3i();
+ }
+
+ Vec3i absMin = getMinBIW(Vectors.grab3i());
+ Vec3i absMax = getMaxBIW(Vectors.grab3i());
+
+ AxisRotations.relativize(absMin, getUp(), absMin);
+ AxisRotations.relativize(absMax, getUp(), absMax);
+
+ Glm.min(absMin, absMax, output);
+
+ Vectors.release(absMax);
+ Vectors.release(absMin);
+
+ return output;
+ }
+
+ default Vec3i getMaxBIWRel(Vec3i output) {
+ if (output == null) {
+ output = new Vec3i();
+ }
+
+ Vec3i absMin = getMinBIW(Vectors.grab3i());
+ Vec3i absMax = getMaxBIW(Vectors.grab3i());
+
+ AxisRotations.relativize(absMin, getUp(), absMin);
+ AxisRotations.relativize(absMax, getUp(), absMax);
+
+ Glm.max(absMin, absMax, output);
+
+ Vectors.release(absMax);
+ Vectors.release(absMin);
+
+ return output;
+ }
default boolean containsBiC(Vec3i blockInChunk) {
return blockInChunk.x >= 0 && blockInChunk.x < BLOCKS_PER_CHUNK &&
diff --git a/src/main/java/ru/windcorp/progressia/server/ChunkManager.java b/src/main/java/ru/windcorp/progressia/server/ChunkManager.java
index b0b0777..ede6d70 100644
--- a/src/main/java/ru/windcorp/progressia/server/ChunkManager.java
+++ b/src/main/java/ru/windcorp/progressia/server/ChunkManager.java
@@ -128,7 +128,7 @@ public class ChunkManager {
private void processQueues() {
toUnload.forEach(this::unloadChunk);
toUnload.clear();
- toLoad.forEach(this::loadChunk);
+ toLoad.forEach(this::loadOrGenerateChunk);
toLoad.clear();
visions.forEach((p, v) -> {
@@ -140,15 +140,26 @@ public class ChunkManager {
return createIfMissing ? visions.computeIfAbsent(player, k -> new PlayerVision()) : visions.get(player);
}
- public void loadChunk(Vec3i chunkPos) {
+ public void loadOrGenerateChunk(Vec3i chunkPos) {
+
+ boolean chunkLoadedFromDisk = loadChunk(chunkPos);
+
+ if (!chunkLoadedFromDisk) {
+ getServer().getWorld().generate(chunkPos);
+ }
+
+ }
+
+ public boolean loadChunk(Vec3i chunkPos) {
WorldData world = getServer().getWorld().getData();
ChunkData chunk = TestWorldDiskIO.tryToLoad(chunkPos, world, getServer());
if (chunk != null) {
world.addChunk(chunk);
+ return true;
} else {
- getServer().getWorld().generate(chunkPos);
+ return false;
}
}
diff --git a/src/main/java/ru/windcorp/progressia/server/Server.java b/src/main/java/ru/windcorp/progressia/server/Server.java
index fcdee16..ceafa47 100644
--- a/src/main/java/ru/windcorp/progressia/server/Server.java
+++ b/src/main/java/ru/windcorp/progressia/server/Server.java
@@ -31,6 +31,7 @@ import ru.windcorp.progressia.server.world.WorldLogic;
import ru.windcorp.progressia.server.world.tasks.WorldAccessor;
import ru.windcorp.progressia.server.world.ticking.Change;
import ru.windcorp.progressia.server.world.ticking.Evaluation;
+import ru.windcorp.progressia.test.gen.planet.Planet;
import ru.windcorp.progressia.test.gen.planet.TestPlanetGenerator;
public class Server {
@@ -60,7 +61,7 @@ public class Server {
private final TickingSettings tickingSettings = new TickingSettings();
public Server(WorldData world) {
- this.world = new WorldLogic(world, this, w -> new TestPlanetGenerator("Test:PlanetGenerator", Units.get("48 m"), w));
+ this.world = new WorldLogic(world, this, w -> new TestPlanetGenerator("Test:PlanetGenerator", new Planet(4, 16f, 9.8f, 16f), w));
this.serverThread = new ServerThread(this);
this.clientManager = new ClientManager(this);
diff --git a/src/main/java/ru/windcorp/progressia/test/gen/TerrainLayer.java b/src/main/java/ru/windcorp/progressia/test/gen/TerrainLayer.java
new file mode 100644
index 0000000..74e3335
--- /dev/null
+++ b/src/main/java/ru/windcorp/progressia/test/gen/TerrainLayer.java
@@ -0,0 +1,30 @@
+/*
+ * Progressia
+ * Copyright (C) 2020-2021 Wind Corporation and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package ru.windcorp.progressia.test.gen;
+
+import java.util.Random;
+
+import ru.windcorp.progressia.common.world.ChunkData;
+import ru.windcorp.progressia.common.world.block.BlockData;
+
+@FunctionalInterface
+public interface TerrainLayer {
+
+ BlockData get(float north, float west, float depth, Random random, ChunkData chunk);
+
+}
diff --git a/src/main/java/ru/windcorp/progressia/test/gen/planet/Planet.java b/src/main/java/ru/windcorp/progressia/test/gen/planet/Planet.java
new file mode 100644
index 0000000..795c85f
--- /dev/null
+++ b/src/main/java/ru/windcorp/progressia/test/gen/planet/Planet.java
@@ -0,0 +1,82 @@
+/*
+ * Progressia
+ * Copyright (C) 2020-2021 Wind Corporation and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package ru.windcorp.progressia.test.gen.planet;
+
+import ru.windcorp.progressia.common.world.ChunkData;
+
+public class Planet {
+
+ private final int radiusInChunks;
+
+ private final float curvature;
+ private final float surfaceGravitationalAcceleration;
+ private final float innerGravityRadius;
+
+ public Planet(
+ int radiusInChunks,
+ float curvature,
+ float surfaceGravitationalAcceleration,
+ float innerGravityRadius
+ ) {
+ this.radiusInChunks = radiusInChunks;
+ this.curvature = curvature;
+ this.surfaceGravitationalAcceleration = surfaceGravitationalAcceleration;
+ this.innerGravityRadius = innerGravityRadius;
+ }
+
+ /**
+ * @return the radiusInChunks
+ */
+ public int getRadiusInChunks() {
+ return radiusInChunks;
+ }
+
+ public float getRadius() {
+ return radiusInChunks * ChunkData.BLOCKS_PER_CHUNK + ChunkData.CHUNK_RADIUS;
+ }
+
+ public int getDiameterInChunks() {
+ return radiusInChunks * 2 + 1;
+ }
+
+ public float getDiameter() {
+ return getDiameterInChunks() * ChunkData.BLOCKS_PER_CHUNK;
+ }
+
+ /**
+ * @return the curvature
+ */
+ public float getCurvature() {
+ return curvature;
+ }
+
+ /**
+ * @return the innerGravityRadius
+ */
+ public float getInnerGravityRadius() {
+ return innerGravityRadius;
+ }
+
+ /**
+ * @return the surfaceGravitationalAcceleration
+ */
+ public float getSurfaceGravitationalAcceleration() {
+ return surfaceGravitationalAcceleration;
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/progressia/test/gen/planet/PlanetScatterGenerator.java b/src/main/java/ru/windcorp/progressia/test/gen/planet/PlanetScatterGenerator.java
new file mode 100644
index 0000000..da0f8d3
--- /dev/null
+++ b/src/main/java/ru/windcorp/progressia/test/gen/planet/PlanetScatterGenerator.java
@@ -0,0 +1,41 @@
+/*
+ * Progressia
+ * Copyright (C) 2020-2021 Wind Corporation and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package ru.windcorp.progressia.test.gen.planet;
+
+import ru.windcorp.progressia.common.world.ChunkData;
+import ru.windcorp.progressia.test.gen.surface.SurfaceScatterGenerator;
+
+public class PlanetScatterGenerator {
+
+ private final TestPlanetGenerator parent;
+ private final SurfaceScatterGenerator surfaceGenerator;
+
+ public PlanetScatterGenerator(TestPlanetGenerator generator) {
+ this.parent = generator;
+ this.surfaceGenerator = new SurfaceScatterGenerator();
+ }
+
+ public TestPlanetGenerator getGenerator() {
+ return parent;
+ }
+
+ public void generateScatter(ChunkData chunk) {
+ surfaceGenerator.generateScatter(chunk);
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/progressia/test/gen/planet/PlanetTerrainGenerator.java b/src/main/java/ru/windcorp/progressia/test/gen/planet/PlanetTerrainGenerator.java
new file mode 100644
index 0000000..d59984a
--- /dev/null
+++ b/src/main/java/ru/windcorp/progressia/test/gen/planet/PlanetTerrainGenerator.java
@@ -0,0 +1,107 @@
+/*
+ * Progressia
+ * Copyright (C) 2020-2021 Wind Corporation and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package ru.windcorp.progressia.test.gen.planet;
+
+import glm.vec._3.Vec3;
+import glm.vec._3.i.Vec3i;
+import ru.windcorp.progressia.common.util.ArrayFloatRangeMap;
+import ru.windcorp.progressia.common.util.FloatRangeMap;
+import ru.windcorp.progressia.common.util.VectorUtil;
+import ru.windcorp.progressia.common.world.ChunkData;
+import ru.windcorp.progressia.common.world.Coordinates;
+import ru.windcorp.progressia.common.world.WorldData;
+import ru.windcorp.progressia.common.world.block.BlockData;
+import ru.windcorp.progressia.common.world.block.BlockDataRegistry;
+import ru.windcorp.progressia.test.gen.TerrainLayer;
+import ru.windcorp.progressia.test.gen.surface.SurfaceFloatField;
+import ru.windcorp.progressia.test.gen.surface.SurfaceTerrainGenerator;
+
+class PlanetTerrainGenerator {
+
+ private final TestPlanetGenerator parent;
+ private final SurfaceTerrainGenerator surfaceGenerator;
+
+ public PlanetTerrainGenerator(TestPlanetGenerator generator) {
+ this.parent = generator;
+
+ SurfaceFloatField heightMap = new TestHeightMap(
+ generator.getPlanet().getRadius() - ChunkData.BLOCKS_PER_CHUNK,
+ generator.getPlanet().getRadius() / 4,
+ 5,
+ 6
+ );
+
+ FloatRangeMap layers = new ArrayFloatRangeMap<>();
+ BlockData granite = BlockDataRegistry.getInstance().get("Test:GraniteMonolith");
+ BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
+ layers.put(Float.NEGATIVE_INFINITY, 0, (n, w, d, r, c) -> air);
+ layers.put(0, Float.POSITIVE_INFINITY, (n, w, d, r, c) -> granite);
+
+ this.surfaceGenerator = new SurfaceTerrainGenerator((f, n, w) -> heightMap.get(f, n, w) + generator.getPlanet().getRadius(), layers);
+ }
+
+ public TestPlanetGenerator getGenerator() {
+ return parent;
+ }
+
+ public ChunkData generateTerrain(Vec3i chunkPos, WorldData world) {
+ ChunkData chunk = new ChunkData(chunkPos, world);
+
+ if (isOrdinaryChunk(chunkPos)) {
+ generateOrdinaryTerrain(chunk);
+ } else {
+ generateBorderTerrain(chunk);
+ }
+
+ return chunk;
+ }
+
+ private boolean isOrdinaryChunk(Vec3i chunkPos) {
+ Vec3i sorted = VectorUtil.sortAfterAbs(chunkPos, null);
+ return sorted.x != sorted.y;
+ }
+
+ private void generateOrdinaryTerrain(ChunkData chunk) {
+ surfaceGenerator.generateTerrain(chunk);
+ }
+
+ private void generateBorderTerrain(ChunkData chunk) {
+ BlockData stone = BlockDataRegistry.getInstance().get("Test:Stone");
+ BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
+
+ float radius = parent.getPlanet().getRadius();
+
+ Vec3 biw = new Vec3();
+
+ chunk.forEachBiC(bic -> {
+
+ biw.set(
+ Coordinates.getInWorld(chunk.getX(), bic.x),
+ Coordinates.getInWorld(chunk.getY(), bic.y),
+ Coordinates.getInWorld(chunk.getZ(), bic.z)
+ );
+
+ biw.sub(ChunkData.CHUNK_RADIUS - 0.5f);
+ VectorUtil.sortAfterAbs(biw, biw);
+
+ chunk.setBlock(bic, biw.x <= radius ? stone : air, false);
+
+ });
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/progressia/test/gen/planet/TestHeightMap.java b/src/main/java/ru/windcorp/progressia/test/gen/planet/TestHeightMap.java
new file mode 100644
index 0000000..2141c63
--- /dev/null
+++ b/src/main/java/ru/windcorp/progressia/test/gen/planet/TestHeightMap.java
@@ -0,0 +1,70 @@
+/*
+ * Progressia
+ * Copyright (C) 2020-2021 Wind Corporation and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package ru.windcorp.progressia.test.gen.planet;
+
+import ru.windcorp.progressia.common.world.rels.AbsFace;
+import ru.windcorp.progressia.test.gen.surface.SurfaceFloatField;
+
+public class TestHeightMap implements SurfaceFloatField {
+
+ private final float cutoffPoint;
+ private final float cutoffDistance;
+ private final float amplitude;
+ private final float characteristicSize;
+
+ public TestHeightMap(
+ float cutoffPoint,
+ float cutoffDistance,
+ float amplitude,
+ float characteristicSize
+ ) {
+ this.cutoffPoint = cutoffPoint;
+ this.cutoffDistance = cutoffDistance;
+ this.amplitude = amplitude;
+ this.characteristicSize = characteristicSize;
+ }
+
+ @Override
+ public float get(AbsFace face, float north, float west) {
+ double cutoffCoefficient = 1;
+ cutoffCoefficient *= cutoffFunction(cutoffPoint - north);
+ cutoffCoefficient *= cutoffFunction(cutoffPoint + north);
+ cutoffCoefficient *= cutoffFunction(cutoffPoint - west);
+ cutoffCoefficient *= cutoffFunction(cutoffPoint + west);
+
+ if (cutoffCoefficient == 0) {
+ return 0;
+ }
+
+ double base = Math.sin(north / characteristicSize) * Math.sin(west / characteristicSize);
+ base *= amplitude;
+
+ return (float) (base * cutoffCoefficient);
+ }
+
+ private double cutoffFunction(float distanceToCutoffPoint) {
+ if (distanceToCutoffPoint < 0) {
+ return 0;
+ } else if (distanceToCutoffPoint < cutoffDistance) {
+ return (1 - Math.cos(Math.PI * distanceToCutoffPoint / cutoffDistance)) / 2;
+ } else {
+ return 1;
+ }
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/progressia/test/gen/planet/TestPlanetGenerator.java b/src/main/java/ru/windcorp/progressia/test/gen/planet/TestPlanetGenerator.java
index ea45d1e..b21d985 100644
--- a/src/main/java/ru/windcorp/progressia/test/gen/planet/TestPlanetGenerator.java
+++ b/src/main/java/ru/windcorp/progressia/test/gen/planet/TestPlanetGenerator.java
@@ -20,38 +20,39 @@ package ru.windcorp.progressia.test.gen.planet;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
-import java.util.Arrays;
-
import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i;
-import ru.windcorp.progressia.common.util.VectorUtil;
import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.DecodingException;
import ru.windcorp.progressia.common.world.WorldData;
-import ru.windcorp.progressia.common.world.block.BlockData;
-import ru.windcorp.progressia.common.world.block.BlockDataRegistry;
-import ru.windcorp.progressia.common.world.rels.RelFace;
-import ru.windcorp.progressia.common.world.tile.TileData;
-import ru.windcorp.progressia.common.world.tile.TileDataRegistry;
import ru.windcorp.progressia.server.world.WorldLogic;
import ru.windcorp.progressia.server.world.generation.AbstractWorldGenerator;
public class TestPlanetGenerator extends AbstractWorldGenerator {
- private final int surfaceLevel;
+ private final Planet planet;
+
+ private final PlanetTerrainGenerator terrainGenerator;
+ private final PlanetScatterGenerator scatterGenerator;
- public TestPlanetGenerator(String id, float planetRadius, WorldLogic world) {
+ public TestPlanetGenerator(String id, Planet planet, WorldLogic world) {
super(id, Boolean.class, "Test:PlanetGravityModel");
+ this.planet = planet;
- this.surfaceLevel = (int) (planetRadius / ChunkData.BLOCKS_PER_CHUNK);
- if (surfaceLevel < 2) {
- throw new IllegalArgumentException("planetRadius too small, must be at least 32 m");
- }
+ this.terrainGenerator = new PlanetTerrainGenerator(this);
+ this.scatterGenerator = new PlanetScatterGenerator(this);
+ }
+
+ /**
+ * @return the planet
+ */
+ public Planet getPlanet() {
+ return planet;
}
@Override
public Vec3 suggestSpawnLocation() {
- return new Vec3(0, 0, 66);
+ return new Vec3(7f, 7f, getPlanet().getRadius() + 10);
}
@Override
@@ -71,116 +72,9 @@ public class TestPlanetGenerator extends AbstractWorldGenerator {
@Override
public ChunkData generate(Vec3i chunkPos, WorldData world) {
- ChunkData chunk = new ChunkData(chunkPos, world);
-
- generate(chunk);
- chunk.setGenerationHint(true);
-
+ ChunkData chunk = terrainGenerator.generateTerrain(chunkPos, world);
+ scatterGenerator.generateScatter(chunk);
return chunk;
}
-
- private enum ChunkType {
- SURFACE, UNDERGROUND, EDGE_SURFACE, EDGE_UNDERGROUND, CORE, AIR;
- }
-
- private void generate(ChunkData chunk) {
- switch (getChunkType(chunk.getPosition())) {
- case SURFACE:
- fillSurface(chunk);
- break;
- case UNDERGROUND:
- fillUndeground(chunk);
- break;
- case EDGE_SURFACE:
- fillEdgeSurface(chunk);
- break;
- case EDGE_UNDERGROUND:
- fillEdgeUnderground(chunk);
- break;
- case CORE:
- fillCore(chunk);
- break;
- case AIR:
- fillAir(chunk);
- break;
- }
- }
-
- private void fillSurface(ChunkData chunk) {
- final int bpc = ChunkData.BLOCKS_PER_CHUNK;
-
- BlockData dirt = BlockDataRegistry.getInstance().get("Test:Dirt");
- BlockData granite = BlockDataRegistry.getInstance().get("Test:GraniteMonolith");
-
- TileData grass = TileDataRegistry.getInstance().get("Test:Grass");
-
- chunk.forEachBiC(bic -> {
-
- BlockData block;
-
- if (bic.z > bpc - 4) {
- block = dirt;
- } else {
- block = granite;
- }
-
- chunk.setBlockRel(bic, block, false);
-
- });
-
- VectorUtil.iterateCuboid(0, 0, bpc - 1, bpc, bpc, bpc, bic -> {
- chunk.getTilesRel(bic, RelFace.UP).add(grass);
- });
- }
-
- private void fillUndeground(ChunkData chunk) {
- fill(chunk, BlockDataRegistry.getInstance().get("Test:GraniteMonolith"));
- }
-
- private void fillEdgeSurface(ChunkData chunk) {
- fill(chunk, BlockDataRegistry.getInstance().get("Test:Stone"));
- }
-
- private void fillEdgeUnderground(ChunkData chunk) {
- fill(chunk, BlockDataRegistry.getInstance().get("Test:Stone"));
- }
-
- private void fillCore(ChunkData chunk) {
- fill(chunk, BlockDataRegistry.getInstance().get("Test:Stone"));
- }
-
- private void fillAir(ChunkData chunk) {
- fill(chunk, BlockDataRegistry.getInstance().get("Test:Air"));
- }
-
- private void fill(ChunkData chunk, BlockData block) {
- chunk.forEachBiC(bic -> chunk.setBlock(bic, block, false));
- }
-
- private ChunkType getChunkType(Vec3i pos) {
- int[] abs = pos.abs_().toIA_();
- Arrays.sort(abs);
-
- int medium = abs[1];
- int largest = abs[2];
-
- int level = largest;
-
- if (level == 0) {
- return ChunkType.CORE;
- }
-
- if (largest > surfaceLevel) {
- return ChunkType.AIR;
- }
-
- boolean isSurface = largest == surfaceLevel;
-
- if (medium == largest) {
- return isSurface ? ChunkType.EDGE_SURFACE : ChunkType.EDGE_UNDERGROUND;
- } else {
- return isSurface ? ChunkType.SURFACE : ChunkType.UNDERGROUND;
- }
- }
}
diff --git a/src/main/java/ru/windcorp/progressia/test/gen/surface/SurfaceFloatField.java b/src/main/java/ru/windcorp/progressia/test/gen/surface/SurfaceFloatField.java
new file mode 100644
index 0000000..4b368d4
--- /dev/null
+++ b/src/main/java/ru/windcorp/progressia/test/gen/surface/SurfaceFloatField.java
@@ -0,0 +1,27 @@
+/*
+ * Progressia
+ * Copyright (C) 2020-2021 Wind Corporation and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package ru.windcorp.progressia.test.gen.surface;
+
+import ru.windcorp.progressia.common.world.rels.AbsFace;
+
+@FunctionalInterface
+public interface SurfaceFloatField {
+
+ float get(AbsFace face, float north, float west);
+
+}
diff --git a/src/main/java/ru/windcorp/progressia/test/gen/surface/SurfaceScatterGenerator.java b/src/main/java/ru/windcorp/progressia/test/gen/surface/SurfaceScatterGenerator.java
new file mode 100644
index 0000000..a9f08d9
--- /dev/null
+++ b/src/main/java/ru/windcorp/progressia/test/gen/surface/SurfaceScatterGenerator.java
@@ -0,0 +1,28 @@
+/*
+ * Progressia
+ * Copyright (C) 2020-2021 Wind Corporation and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package ru.windcorp.progressia.test.gen.surface;
+
+import ru.windcorp.progressia.common.world.ChunkData;
+
+public class SurfaceScatterGenerator {
+
+ public void generateScatter(ChunkData chunk) {
+ chunk.setGenerationHint(true);
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/progressia/test/gen/surface/SurfaceTerrainGenerator.java b/src/main/java/ru/windcorp/progressia/test/gen/surface/SurfaceTerrainGenerator.java
new file mode 100644
index 0000000..b05df5a
--- /dev/null
+++ b/src/main/java/ru/windcorp/progressia/test/gen/surface/SurfaceTerrainGenerator.java
@@ -0,0 +1,74 @@
+/*
+ * Progressia
+ * Copyright (C) 2020-2021 Wind Corporation and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package ru.windcorp.progressia.test.gen.surface;
+
+import java.util.Random;
+
+import glm.vec._3.Vec3;
+import glm.vec._3.i.Vec3i;
+import ru.windcorp.progressia.common.util.CoordinatePacker;
+import ru.windcorp.progressia.common.util.FloatRangeMap;
+import ru.windcorp.progressia.common.world.ChunkData;
+import ru.windcorp.progressia.common.world.block.BlockData;
+import ru.windcorp.progressia.common.world.rels.AxisRotations;
+import ru.windcorp.progressia.test.gen.TerrainLayer;
+
+public class SurfaceTerrainGenerator {
+
+ private final SurfaceFloatField heightMap;
+ private final FloatRangeMap layers;
+
+ public SurfaceTerrainGenerator(SurfaceFloatField heightMap, FloatRangeMap layers) {
+ this.heightMap = heightMap;
+ this.layers = layers;
+ }
+
+ public void generateTerrain(ChunkData chunk) {
+
+ Vec3i relBIC = new Vec3i();
+
+ Vec3 offset = new Vec3(chunk.getMinX(), chunk.getMinY(), chunk.getMinZ());
+ AxisRotations.relativize(offset, chunk.getUp(), offset);
+ offset.sub(ChunkData.CHUNK_RADIUS - 0.5f);
+
+ Random random = new Random(CoordinatePacker.pack3IntsIntoLong(chunk.getPosition()) /* ^ seed*/);
+
+ for (relBIC.x = 0; relBIC.x < ChunkData.BLOCKS_PER_CHUNK; ++relBIC.x) {
+ for (relBIC.y = 0; relBIC.y < ChunkData.BLOCKS_PER_CHUNK; ++relBIC.y) {
+ generateColumn(chunk, relBIC, offset, random);
+ }
+ }
+
+ }
+
+ public void generateColumn(ChunkData chunk, Vec3i relBIC, Vec3 offset, Random random) {
+
+ float north = relBIC.x + offset.x;
+ float west = relBIC.y + offset.y;
+
+ float relSurface = heightMap.get(chunk.getUp(), north, west) - offset.z;
+
+ for (relBIC.z = 0; relBIC.z < ChunkData.BLOCKS_PER_CHUNK; ++relBIC.z) {
+ float depth = relSurface - relBIC.z;
+ BlockData block = layers.get(depth).get(north, west, depth, random, chunk);
+ chunk.setBlockRel(relBIC, block, false);
+ }
+
+ }
+
+}