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:
OLEGSHA 2021-03-15 18:54:53 +03:00
parent abd8d9eebb
commit f4311fb27c
Signed by: OLEGSHA
GPG Key ID: E57A4B08D64AFF7A
16 changed files with 896 additions and 133 deletions

View File

@ -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++;
}
}
}
}

View File

@ -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;
}
}

View File

@ -291,6 +291,88 @@ public class VectorUtil {
return output; 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) { public static float get(Vec2 v, Axis a) {
switch (a) { switch (a) {
case X: case X:

View File

@ -90,11 +90,7 @@ public class BlockRay {
VectorUtil.set(block, axis, VectorUtil.get(block, axis) + (int) signum(VectorUtil.get(direction, axis))); VectorUtil.set(block, axis, VectorUtil.get(block, axis) + (int) signum(VectorUtil.get(direction, axis)));
// position += direction * tMin // position += direction * tMin
VectorUtil.linearCombination(position, 1, direction, tMin, position); // position VectorUtil.linearCombination(position, 1, direction, tMin, position);
// +=
// direction
// *
// tMin
distance += tMin; distance += tMin;
// position.(axis) = round(position.(axis)) // position.(axis) = round(position.(axis))

View File

@ -20,6 +20,7 @@ package ru.windcorp.progressia.common.world.generic;
import java.util.function.Consumer; import java.util.function.Consumer;
import glm.Glm;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.util.VectorUtil; import ru.windcorp.progressia.common.util.VectorUtil;
import ru.windcorp.progressia.common.util.Vectors; 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); 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) { default boolean containsBiC(Vec3i blockInChunk) {
return blockInChunk.x >= 0 && blockInChunk.x < BLOCKS_PER_CHUNK && return blockInChunk.x >= 0 && blockInChunk.x < BLOCKS_PER_CHUNK &&
blockInChunk.y >= 0 && blockInChunk.y < BLOCKS_PER_CHUNK && blockInChunk.y >= 0 && blockInChunk.y < BLOCKS_PER_CHUNK &&

View File

@ -128,7 +128,7 @@ public class ChunkManager {
private void processQueues() { private void processQueues() {
toUnload.forEach(this::unloadChunk); toUnload.forEach(this::unloadChunk);
toUnload.clear(); toUnload.clear();
toLoad.forEach(this::loadChunk); toLoad.forEach(this::loadOrGenerateChunk);
toLoad.clear(); toLoad.clear();
visions.forEach((p, v) -> { visions.forEach((p, v) -> {
@ -140,15 +140,26 @@ public class ChunkManager {
return createIfMissing ? visions.computeIfAbsent(player, k -> new PlayerVision()) : visions.get(player); 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(); WorldData world = getServer().getWorld().getData();
ChunkData chunk = TestWorldDiskIO.tryToLoad(chunkPos, world, getServer()); ChunkData chunk = TestWorldDiskIO.tryToLoad(chunkPos, world, getServer());
if (chunk != null) { if (chunk != null) {
world.addChunk(chunk); world.addChunk(chunk);
return true;
} else { } else {
getServer().getWorld().generate(chunkPos); return false;
} }
} }

View File

@ -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.tasks.WorldAccessor;
import ru.windcorp.progressia.server.world.ticking.Change; import ru.windcorp.progressia.server.world.ticking.Change;
import ru.windcorp.progressia.server.world.ticking.Evaluation; import ru.windcorp.progressia.server.world.ticking.Evaluation;
import ru.windcorp.progressia.test.gen.planet.Planet;
import ru.windcorp.progressia.test.gen.planet.TestPlanetGenerator; import ru.windcorp.progressia.test.gen.planet.TestPlanetGenerator;
public class Server { public class Server {
@ -60,7 +61,7 @@ public class Server {
private final TickingSettings tickingSettings = new TickingSettings(); private final TickingSettings tickingSettings = new TickingSettings();
public Server(WorldData world) { 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.serverThread = new ServerThread(this);
this.clientManager = new ClientManager(this); this.clientManager = new ClientManager(this);

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
});
}
}

View File

@ -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;
}
}
}

View File

@ -20,38 +20,39 @@ package ru.windcorp.progressia.test.gen.planet;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i; 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.ChunkData;
import ru.windcorp.progressia.common.world.DecodingException; import ru.windcorp.progressia.common.world.DecodingException;
import ru.windcorp.progressia.common.world.WorldData; 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.WorldLogic;
import ru.windcorp.progressia.server.world.generation.AbstractWorldGenerator; import ru.windcorp.progressia.server.world.generation.AbstractWorldGenerator;
public class TestPlanetGenerator extends AbstractWorldGenerator<Boolean> { 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"); super(id, Boolean.class, "Test:PlanetGravityModel");
this.planet = planet;
this.surfaceLevel = (int) (planetRadius / ChunkData.BLOCKS_PER_CHUNK); this.terrainGenerator = new PlanetTerrainGenerator(this);
if (surfaceLevel < 2) { this.scatterGenerator = new PlanetScatterGenerator(this);
throw new IllegalArgumentException("planetRadius too small, must be at least 32 m");
} }
/**
* @return the planet
*/
public Planet getPlanet() {
return planet;
} }
@Override @Override
public Vec3 suggestSpawnLocation() { public Vec3 suggestSpawnLocation() {
return new Vec3(0, 0, 66); return new Vec3(7f, 7f, getPlanet().getRadius() + 10);
} }
@Override @Override
@ -71,116 +72,9 @@ public class TestPlanetGenerator extends AbstractWorldGenerator<Boolean> {
@Override @Override
public ChunkData generate(Vec3i chunkPos, WorldData world) { public ChunkData generate(Vec3i chunkPos, WorldData world) {
ChunkData chunk = new ChunkData(chunkPos, world); ChunkData chunk = terrainGenerator.generateTerrain(chunkPos, world);
scatterGenerator.generateScatter(chunk);
generate(chunk);
chunk.setGenerationHint(true);
return 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;
}
}
} }

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}