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