Created a bare-bones implementation of the final planet generator
- Added Planet generator - Uses temporary generation algorithms - Added Surface generator - Added FloatRangeMap
This commit is contained in:
parent
abd8d9eebb
commit
f4311fb27c
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<E> implements FloatRangeMap<E> {
|
||||
|
||||
protected static class Node<E> implements Comparable<Node<E>> {
|
||||
public float pos;
|
||||
public E value;
|
||||
|
||||
public Node(float pos, E value) {
|
||||
this.pos = pos;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Node<E> o) {
|
||||
return Float.compare(pos, o.pos);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Expects a random-access list
|
||||
*/
|
||||
protected final List<Node<E>> 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<E> iterator() {
|
||||
return new Iterator<E>() {
|
||||
|
||||
private int nextIndex = 0;
|
||||
|
||||
{
|
||||
assert nodes.isEmpty() || nodes.get(nextIndex).value != null;
|
||||
}
|
||||
|
||||
private void findNext() {
|
||||
while (nextIndex < nodes.size()) {
|
||||
nextIndex++;
|
||||
Node<E> 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<E> getEffectiveNode(float at) {
|
||||
int effectiveNodeIndex = findFloor(at);
|
||||
if (effectiveNodeIndex < 0) return null;
|
||||
return nodes.get(effectiveNodeIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get(float at) {
|
||||
Node<E> 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<E>(min, element));
|
||||
ranges++;
|
||||
|
||||
ListIterator<Node<E>> it = nodes.listIterator(indexOfInsertionOfMin + 1);
|
||||
E elementEffectiveImmediatelyAfterInsertedRange = null;
|
||||
|
||||
if (indexOfInsertionOfMin > 0) {
|
||||
elementEffectiveImmediatelyAfterInsertedRange = nodes.get(indexOfInsertionOfMin - 1).value;
|
||||
}
|
||||
|
||||
while (it.hasNext()) {
|
||||
Node<E> 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<E>(max, elementEffectiveImmediatelyAfterInsertedRange));
|
||||
|
||||
if (elementEffectiveImmediatelyAfterInsertedRange != null) {
|
||||
// We might have added one right back
|
||||
ranges++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package ru.windcorp.progressia.common.util;
|
||||
|
||||
public interface FloatRangeMap<E> extends Iterable<E> {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -291,6 +291,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) {
|
||||
case X:
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
@ -119,6 +120,64 @@ public interface GenericChunk<Self extends GenericChunk<Self, B, T, TS>, B exten
|
||||
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 &&
|
||||
blockInChunk.y >= 0 && blockInChunk.y < BLOCKS_PER_CHUNK &&
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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);
|
||||
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<TerrainLayer> 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);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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<Boolean> {
|
||||
|
||||
private final int surfaceLevel;
|
||||
private final Planet planet;
|
||||
|
||||
public TestPlanetGenerator(String id, float planetRadius, WorldLogic world) {
|
||||
private final PlanetTerrainGenerator terrainGenerator;
|
||||
private final PlanetScatterGenerator scatterGenerator;
|
||||
|
||||
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<Boolean> {
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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);
|
||||
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<TerrainLayer> layers;
|
||||
|
||||
public SurfaceTerrainGenerator(SurfaceFloatField heightMap, FloatRangeMap<TerrainLayer> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user