Added rock DB and worldgen
- Added Rocks container - Added DiscreteNoise and a DIY Worley generator - Added RockLayer - Used to generate rock strata - Reworked SurfaceTerrainGenerator to use contexts
This commit is contained in:
parent
4f620b7261
commit
9fc1a21191
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.noise.discrete;
|
||||
|
||||
public interface DiscreteNoise<T> {
|
||||
|
||||
T get(double x, double y);
|
||||
T get(double x, double y, double z);
|
||||
|
||||
}
|
@ -0,0 +1,228 @@
|
||||
/*
|
||||
* 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.noise.discrete;
|
||||
|
||||
import java.util.Collection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class WorleyProceduralNoise<T> implements DiscreteNoise<T> {
|
||||
|
||||
/*
|
||||
* Stolen from OpenJDK's Random implementation
|
||||
* *evil cackling*
|
||||
*/
|
||||
private static final long MULTIPLIER = 0x5DEECE66DL;
|
||||
private static final long ADDEND = 0xBL;
|
||||
private static final long MASK = (1L << 48) - 1;
|
||||
private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53)
|
||||
|
||||
private static long permute(long seed) {
|
||||
return (seed * MULTIPLIER + ADDEND) & MASK;
|
||||
}
|
||||
|
||||
private static double getDouble(long seed) {
|
||||
final int mask26bits = (1 << 26) - 1;
|
||||
final int mask27bits = (1 << 27) - 1;
|
||||
|
||||
int randomBitsX26 = (int) (seed & 0xFFFFFFFF);
|
||||
int randomBitsX27 = (int) ((seed >>> Integer.SIZE) & 0xFFFFFFFF);
|
||||
|
||||
randomBitsX26 = randomBitsX26 & mask26bits;
|
||||
randomBitsX27 = randomBitsX27 & mask27bits;
|
||||
|
||||
return (((long) (randomBitsX26) << 27) + randomBitsX27) * DOUBLE_UNIT;
|
||||
}
|
||||
|
||||
public static class Entry<T> {
|
||||
private final T value;
|
||||
private final double chance;
|
||||
|
||||
public Entry(T value, double chance) {
|
||||
this.value = value;
|
||||
this.chance = chance;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder<T> {
|
||||
|
||||
com.google.common.collect.ImmutableList.Builder<Entry<T>> builder = ImmutableList.builder();
|
||||
|
||||
public Builder<T> add(T value, double chance) {
|
||||
builder.add(new Entry<>(value, chance));
|
||||
return this;
|
||||
}
|
||||
|
||||
public WorleyProceduralNoise<T> build(long seed) {
|
||||
return new WorleyProceduralNoise<>(this, seed);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static <T> Builder<T> builder() {
|
||||
return new Builder<>();
|
||||
}
|
||||
|
||||
private final Entry<?>[] entries;
|
||||
private final long seed;
|
||||
|
||||
public WorleyProceduralNoise(Builder<T> builder, long seed) {
|
||||
this(builder.builder.build(), seed);
|
||||
}
|
||||
|
||||
public WorleyProceduralNoise(Collection<? extends Entry<? extends T>> entries, long seed) {
|
||||
this.entries = new Entry<?>[entries.size()];
|
||||
|
||||
double chancesSum = 0;
|
||||
for (Entry<? extends T> entry : entries) {
|
||||
chancesSum += entry.chance;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (Entry<? extends T> entry : entries) {
|
||||
this.entries[i] = new Entry<T>(entry.value, entry.chance / chancesSum);
|
||||
i++;
|
||||
}
|
||||
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(double x, double y) {
|
||||
|
||||
int ox = (int) x;
|
||||
int oy = (int) y;
|
||||
|
||||
T closest = null;
|
||||
double closestDistanceSq = Double.POSITIVE_INFINITY;
|
||||
|
||||
for (int cellX = ox - 1; cellX <= ox + 1; ++cellX) {
|
||||
for (int cellY = oy - 1; cellY <= oy + 1; ++cellY) {
|
||||
|
||||
long cellSeed = permute(cellY ^ permute(cellX ^ seed));
|
||||
|
||||
int nodes = getNodeCount(cellSeed);
|
||||
cellSeed = permute(cellSeed);
|
||||
|
||||
for (int i = 0; i < nodes; ++i) {
|
||||
|
||||
double nodeX = getDouble(cellSeed) + cellX;
|
||||
cellSeed = permute(cellSeed);
|
||||
|
||||
double nodeY = getDouble(cellSeed) + cellY;
|
||||
cellSeed = permute(cellSeed);
|
||||
|
||||
T value = getValue(getDouble(cellSeed));
|
||||
cellSeed = permute(cellSeed);
|
||||
|
||||
double distanceSq = (x - nodeX) * (x - nodeX) + (y - nodeY) * (y - nodeY);
|
||||
if (distanceSq < closestDistanceSq) {
|
||||
closestDistanceSq = distanceSq;
|
||||
closest = value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(double x, double y, double z) {
|
||||
|
||||
int ox = (int) x;
|
||||
int oy = (int) y;
|
||||
int oz = (int) z;
|
||||
|
||||
T closest = null;
|
||||
double closestDistanceSq = Double.POSITIVE_INFINITY;
|
||||
|
||||
for (int cellX = ox - 1; cellX <= ox + 1; ++cellX) {
|
||||
for (int cellY = oy - 1; cellY <= oy + 1; ++cellY) {
|
||||
for (int cellZ = oz - 1; cellZ <= oz + 1; ++cellZ) {
|
||||
|
||||
long cellSeed = permute(cellZ ^ permute(cellY ^ permute(cellX ^ seed)));
|
||||
|
||||
int nodes = getNodeCount(cellSeed);
|
||||
cellSeed = permute(cellSeed);
|
||||
|
||||
for (int i = 0; i < nodes; ++i) {
|
||||
|
||||
double nodeX = getDouble(cellSeed) + cellX;
|
||||
cellSeed = permute(cellSeed);
|
||||
|
||||
double nodeY = getDouble(cellSeed) + cellY;
|
||||
cellSeed = permute(cellSeed);
|
||||
|
||||
double nodeZ = getDouble(cellSeed) + cellZ;
|
||||
cellSeed = permute(cellSeed);
|
||||
|
||||
T value = getValue(getDouble(cellSeed));
|
||||
cellSeed = permute(cellSeed);
|
||||
|
||||
double distanceSq = (x - nodeX) * (x - nodeX) + (y - nodeY) * (y - nodeY)
|
||||
+ (z - nodeZ) * (z - nodeZ);
|
||||
if (distanceSq < closestDistanceSq) {
|
||||
closestDistanceSq = distanceSq;
|
||||
closest = value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private T getValue(double target) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < entries.length && target > entries[i].chance; ++i) {
|
||||
target -= entries[i].chance;
|
||||
}
|
||||
|
||||
return (T) entries[i].value;
|
||||
}
|
||||
|
||||
private int getNodeCount(long seed) {
|
||||
int uniform = ((int) seed) % 8;
|
||||
|
||||
switch (uniform) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
return 1;
|
||||
|
||||
case 4:
|
||||
case 5:
|
||||
return 2;
|
||||
|
||||
case 6:
|
||||
return 3;
|
||||
|
||||
default:
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -104,7 +104,7 @@ public class PlanetGenerator extends AbstractWorldGenerator<Boolean> {
|
||||
DefaultChunkData chunk = getWorldData().getChunk(chunkPos);
|
||||
|
||||
if (chunk == null) {
|
||||
chunk = terrainGenerator.generateTerrain(chunkPos);
|
||||
chunk = terrainGenerator.generateTerrain(getServer(), chunkPos);
|
||||
getWorldData().addChunk(chunk);
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
*/
|
||||
package ru.windcorp.progressia.server.world.generation.planet;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.util.FloatRangeMap;
|
||||
@ -26,6 +28,9 @@ import ru.windcorp.progressia.common.world.Coordinates;
|
||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockDataRegistry;
|
||||
import ru.windcorp.progressia.common.world.generic.GenericChunks;
|
||||
import ru.windcorp.progressia.common.world.rels.AbsFace;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
import ru.windcorp.progressia.server.world.generation.surface.Surface;
|
||||
import ru.windcorp.progressia.server.world.generation.surface.SurfaceFloatField;
|
||||
import ru.windcorp.progressia.server.world.generation.surface.SurfaceTerrainGenerator;
|
||||
import ru.windcorp.progressia.server.world.generation.surface.TerrainLayer;
|
||||
@ -33,25 +38,38 @@ import ru.windcorp.progressia.server.world.generation.surface.TerrainLayer;
|
||||
class PlanetTerrainGenerator {
|
||||
|
||||
private final PlanetGenerator parent;
|
||||
private final SurfaceTerrainGenerator surfaceGenerator;
|
||||
private final Map<AbsFace, SurfaceTerrainGenerator> surfaceGenerators;
|
||||
|
||||
public PlanetTerrainGenerator(PlanetGenerator generator, SurfaceFloatField heightMap, FloatRangeMap<TerrainLayer> layers) {
|
||||
public PlanetTerrainGenerator(
|
||||
PlanetGenerator generator,
|
||||
SurfaceFloatField heightMap,
|
||||
FloatRangeMap<TerrainLayer> layers
|
||||
) {
|
||||
this.parent = generator;
|
||||
|
||||
int seaLevel = (int) parent.getPlanet().getRadius();
|
||||
SurfaceFloatField adjustedHeightMap = (f, n, w) -> heightMap.get(f, n, w) + generator.getPlanet().getRadius();
|
||||
this.surfaceGenerator = new SurfaceTerrainGenerator(adjustedHeightMap, layers);
|
||||
|
||||
this.surfaceGenerators = AbsFace.mapToFaces(
|
||||
face -> new SurfaceTerrainGenerator(
|
||||
new Surface(face, seaLevel),
|
||||
adjustedHeightMap,
|
||||
layers
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public PlanetGenerator getGenerator() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public DefaultChunkData generateTerrain(Vec3i chunkPos) {
|
||||
public DefaultChunkData generateTerrain(Server server, Vec3i chunkPos) {
|
||||
DefaultChunkData chunk = new DefaultChunkData(chunkPos, getGenerator().getWorldData());
|
||||
|
||||
if (isOrdinaryChunk(chunkPos)) {
|
||||
generateOrdinaryTerrain(chunk);
|
||||
generateOrdinaryTerrain(server, chunk);
|
||||
} else {
|
||||
generateBorderTerrain(chunk);
|
||||
generateBorderTerrain(server, chunk);
|
||||
}
|
||||
|
||||
chunk.setGenerationHint(false);
|
||||
@ -64,11 +82,11 @@ class PlanetTerrainGenerator {
|
||||
return sorted.x != sorted.y;
|
||||
}
|
||||
|
||||
private void generateOrdinaryTerrain(DefaultChunkData chunk) {
|
||||
surfaceGenerator.generateTerrain(chunk);
|
||||
private void generateOrdinaryTerrain(Server server, DefaultChunkData chunk) {
|
||||
surfaceGenerators.get(chunk.getUp()).generateTerrain(server, chunk);
|
||||
}
|
||||
|
||||
private void generateBorderTerrain(DefaultChunkData chunk) {
|
||||
private void generateBorderTerrain(Server server, DefaultChunkData chunk) {
|
||||
BlockData stone = BlockDataRegistry.getInstance().get("Test:Stone");
|
||||
BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
|
||||
|
||||
|
@ -17,7 +17,17 @@
|
||||
*/
|
||||
package ru.windcorp.progressia.server.world.generation.surface;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import glm.Glm;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.util.CoordinatePacker;
|
||||
import ru.windcorp.progressia.common.world.generic.ChunkGenericRO;
|
||||
import ru.windcorp.progressia.common.world.rels.AbsFace;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
import ru.windcorp.progressia.server.world.context.ServerTileContext;
|
||||
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceContextImpl;
|
||||
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceWorldContext;
|
||||
|
||||
public class Surface {
|
||||
|
||||
@ -43,4 +53,27 @@ public class Surface {
|
||||
return seaLevel;
|
||||
}
|
||||
|
||||
public SurfaceWorldContext createContext(Server server, ChunkGenericRO<?, ?, ?, ?, ?> chunk, long seed) {
|
||||
|
||||
Random random = new Random(CoordinatePacker.pack3IntsIntoLong(chunk.getPosition()) ^ seed);
|
||||
|
||||
SurfaceContextImpl context = new SurfaceContextImpl((ServerTileContext) server.createAbsoluteContext(), this);
|
||||
context.setRandom(random);
|
||||
|
||||
Vec3i tmpA = new Vec3i();
|
||||
Vec3i tmpB = new Vec3i();
|
||||
|
||||
chunk.getMinBIW(tmpA);
|
||||
chunk.getMaxBIW(tmpB);
|
||||
|
||||
context.toContext(tmpA, tmpA);
|
||||
context.toContext(tmpB, tmpB);
|
||||
|
||||
Glm.min(tmpA, tmpB, context.getMin());
|
||||
Glm.max(tmpA, tmpB, context.getMax());
|
||||
|
||||
return context;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,15 +19,9 @@ package ru.windcorp.progressia.server.world.generation.surface;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import glm.Glm;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.util.CoordinatePacker;
|
||||
import ru.windcorp.progressia.common.world.DefaultChunkData;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
import ru.windcorp.progressia.server.world.context.ServerTileContext;
|
||||
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceContextImpl;
|
||||
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceWorldContext;
|
||||
|
||||
public class SurfaceFeatureGenerator {
|
||||
|
||||
@ -48,23 +42,7 @@ public class SurfaceFeatureGenerator {
|
||||
}
|
||||
|
||||
public void generateFeatures(Server server, DefaultChunkData chunk) {
|
||||
|
||||
Random random = new Random(CoordinatePacker.pack3IntsIntoLong(chunk.getPosition()) /* ^ seed*/);
|
||||
|
||||
SurfaceContextImpl context = new SurfaceContextImpl((ServerTileContext) server.createAbsoluteContext(), surface);
|
||||
context.setRandom(random);
|
||||
|
||||
Vec3i tmpA = new Vec3i();
|
||||
Vec3i tmpB = new Vec3i();
|
||||
|
||||
chunk.getMinBIW(tmpA);
|
||||
chunk.getMaxBIW(tmpB);
|
||||
|
||||
context.toContext(tmpA, tmpA);
|
||||
context.toContext(tmpB, tmpB);
|
||||
|
||||
Glm.min(tmpA, tmpB, context.getMin());
|
||||
Glm.max(tmpA, tmpB, context.getMax());
|
||||
SurfaceWorldContext context = surface.createContext(server, chunk, 0);
|
||||
|
||||
for (SurfaceFeature feature : features) {
|
||||
feature.process(context);
|
||||
|
@ -17,60 +17,73 @@
|
||||
*/
|
||||
package ru.windcorp.progressia.server.world.generation.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.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.DefaultChunkData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
import ru.windcorp.progressia.common.world.rels.AxisRotations;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceBlockContext;
|
||||
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceWorldContext;
|
||||
|
||||
public class SurfaceTerrainGenerator {
|
||||
|
||||
private final Surface surface;
|
||||
|
||||
private final SurfaceFloatField heightMap;
|
||||
private final FloatRangeMap<TerrainLayer> layers;
|
||||
|
||||
public SurfaceTerrainGenerator(SurfaceFloatField heightMap, FloatRangeMap<TerrainLayer> layers) {
|
||||
public SurfaceTerrainGenerator(Surface surface, SurfaceFloatField heightMap, FloatRangeMap<TerrainLayer> layers) {
|
||||
this.surface = surface;
|
||||
this.heightMap = heightMap;
|
||||
this.layers = layers;
|
||||
}
|
||||
|
||||
public void generateTerrain(DefaultChunkData chunk) {
|
||||
public void generateTerrain(Server server, DefaultChunkData chunk) {
|
||||
|
||||
Vec3i relBIC = new Vec3i();
|
||||
|
||||
Vec3 offset = new Vec3(chunk.getMinX(), chunk.getMinY(), chunk.getMinZ());
|
||||
AxisRotations.relativize(offset, chunk.getUp(), offset);
|
||||
offset.z -= DefaultChunkData.CHUNK_RADIUS - 0.5f;
|
||||
|
||||
Random random = new Random(CoordinatePacker.pack3IntsIntoLong(chunk.getPosition()) /* ^ seed*/);
|
||||
SurfaceWorldContext context = surface.createContext(server, chunk, 0);
|
||||
|
||||
for (relBIC.x = 0; relBIC.x < DefaultChunkData.BLOCKS_PER_CHUNK; ++relBIC.x) {
|
||||
for (relBIC.y = 0; relBIC.y < DefaultChunkData.BLOCKS_PER_CHUNK; ++relBIC.y) {
|
||||
generateColumn(chunk, relBIC, offset, random);
|
||||
generateColumn(chunk, relBIC, offset, context);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void generateColumn(DefaultChunkData chunk, Vec3i relBIC, Vec3 offset, Random random) {
|
||||
public void generateColumn(DefaultChunkData chunk, Vec3i relBIC, Vec3 offset, SurfaceWorldContext context) {
|
||||
|
||||
float north = relBIC.x + offset.x;
|
||||
float west = relBIC.y + offset.y;
|
||||
int north = (int) (relBIC.x + offset.x);
|
||||
int west = (int) (relBIC.y + offset.y);
|
||||
|
||||
float relSurface = heightMap.get(chunk.getUp(), north, west) - offset.z;
|
||||
float relSurface = heightMap.get(chunk.getUp(), north, west) - offset.z + DefaultChunkData.CHUNK_RADIUS - 0.5f;
|
||||
Vec3i location = Vectors.grab3i();
|
||||
|
||||
for (relBIC.z = 0; relBIC.z < DefaultChunkData.BLOCKS_PER_CHUNK; ++relBIC.z) {
|
||||
float depth = relSurface - relBIC.z;
|
||||
BlockData block = layers.get(depth).get(chunk.getUp(), north, west, depth, random);
|
||||
int altitude = (int) (relBIC.z + offset.z);
|
||||
|
||||
location.set(north, west, altitude);
|
||||
SurfaceBlockContext blockContext = context.push(location);
|
||||
|
||||
BlockData block = layers.get(depth).get(blockContext, depth);
|
||||
|
||||
blockContext.pop();
|
||||
|
||||
chunk.resolve(relBIC, relBIC);
|
||||
chunk.setBlock(relBIC, block, false);
|
||||
chunk.relativize(relBIC, relBIC);
|
||||
}
|
||||
|
||||
Vectors.release(location);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,14 +17,12 @@
|
||||
*/
|
||||
package ru.windcorp.progressia.server.world.generation.surface;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
import ru.windcorp.progressia.common.world.rels.AbsFace;
|
||||
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceBlockContext;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TerrainLayer {
|
||||
|
||||
BlockData get(AbsFace face, float north, float west, float depth, Random random);
|
||||
BlockData get(SurfaceBlockContext context, float depth);
|
||||
|
||||
}
|
||||
|
126
src/main/java/ru/windcorp/progressia/test/Rocks.java
Normal file
126
src/main/java/ru/windcorp/progressia/test/Rocks.java
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
|
||||
import ru.windcorp.progressia.client.world.block.BlockRenderOpaqueCube;
|
||||
import ru.windcorp.progressia.client.world.block.BlockRenderRegistry;
|
||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockDataRegistry;
|
||||
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
||||
import ru.windcorp.progressia.server.world.block.BlockLogicRegistry;
|
||||
|
||||
public class Rocks {
|
||||
|
||||
public enum RockType {
|
||||
IGNEOUS, METAMORPHIC, SEDIMENTARY;
|
||||
}
|
||||
|
||||
public enum RockVariant {
|
||||
|
||||
MONOLITH("Monolith"),
|
||||
CRACKED("Cracked"),
|
||||
GRAVEL("Gravel"),
|
||||
SAND("Sand");
|
||||
|
||||
private final String name;
|
||||
|
||||
private RockVariant(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Rock {
|
||||
|
||||
private final String name;
|
||||
private final RockType type;
|
||||
|
||||
private final Map<RockVariant, BlockData> blocks = new EnumMap<>(RockVariant.class);
|
||||
|
||||
public Rock(String name, RockType type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public RockType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public BlockData getBlock(RockVariant variant) {
|
||||
return blocks.get(variant);
|
||||
}
|
||||
|
||||
private void register() {
|
||||
for (RockVariant variant : RockVariant.values()) {
|
||||
|
||||
String fullName = name + variant.getName();
|
||||
String id = "Test:" + fullName;
|
||||
|
||||
BlockData blockData = new BlockData(id);
|
||||
blocks.put(variant, blockData);
|
||||
BlockDataRegistry.getInstance().register(blockData);
|
||||
BlockLogicRegistry.getInstance().register(new BlockLogic(id));
|
||||
BlockRenderRegistry.getInstance()
|
||||
.register(new BlockRenderOpaqueCube(id, BlockRenderRegistry.getBlockTexture(fullName)));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final Map<String, Rock> rocksByName = Collections.synchronizedMap(new HashMap<>());
|
||||
private final Multimap<RockType, Rock> rocksByType = Multimaps.synchronizedMultimap(HashMultimap.create());
|
||||
|
||||
public Rock create(RockType type, String name) {
|
||||
Rock rock = new Rock(name, type);
|
||||
rocksByName.put(name, rock);
|
||||
rocksByType.put(type, rock);
|
||||
return rock;
|
||||
}
|
||||
|
||||
public void registerAllRocks() {
|
||||
getRocks().forEach(Rock::register);
|
||||
}
|
||||
|
||||
public Collection<Rock> getRocks() {
|
||||
return rocksByName.values();
|
||||
}
|
||||
|
||||
public Collection<Rock> getRocks(RockType type) {
|
||||
return rocksByType.get(type);
|
||||
}
|
||||
|
||||
}
|
@ -29,8 +29,6 @@ import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.client.ClientState;
|
||||
import ru.windcorp.progressia.client.audio.Sound;
|
||||
@ -59,6 +57,7 @@ import ru.windcorp.progressia.server.world.block.*;
|
||||
import ru.windcorp.progressia.server.world.entity.*;
|
||||
import ru.windcorp.progressia.server.world.generation.planet.PlanetGravityModel;
|
||||
import ru.windcorp.progressia.server.world.tile.*;
|
||||
import ru.windcorp.progressia.test.Rocks.RockType;
|
||||
import ru.windcorp.progressia.test.gen.TestGravityModel;
|
||||
|
||||
public class TestContent {
|
||||
@ -70,6 +69,8 @@ public class TestContent {
|
||||
public static final List<BlockData> PLACEABLE_BLOCKS = new ArrayList<>();
|
||||
public static final List<TileData> PLACEABLE_TILES = new ArrayList<>();
|
||||
|
||||
public static final Rocks ROCKS = new Rocks();
|
||||
|
||||
public static void registerContent() {
|
||||
registerWorldContent();
|
||||
regsiterControls();
|
||||
@ -150,33 +151,16 @@ public class TestContent {
|
||||
}
|
||||
|
||||
private static void registerRocks() {
|
||||
List<String> rockNames = ImmutableList.of(
|
||||
"BlackGranite",
|
||||
"Dolomite",
|
||||
"Eclogite",
|
||||
"Gabbro",
|
||||
"Limestone",
|
||||
"Marble",
|
||||
"RedGranite"
|
||||
);
|
||||
|
||||
List<String> rockVariants = ImmutableList.of(
|
||||
"Monolith",
|
||||
"Cracked",
|
||||
"Gravel",
|
||||
"Sand"
|
||||
);
|
||||
ROCKS.create(RockType.IGNEOUS, "BlackGranite");
|
||||
ROCKS.create(RockType.IGNEOUS, "RedGranite");
|
||||
ROCKS.create(RockType.IGNEOUS, "Gabbro");
|
||||
ROCKS.create(RockType.METAMORPHIC, "Marble");
|
||||
ROCKS.create(RockType.METAMORPHIC, "Eclogite");
|
||||
ROCKS.create(RockType.SEDIMENTARY, "Limestone");
|
||||
ROCKS.create(RockType.SEDIMENTARY, "Dolomite");
|
||||
|
||||
for (String name : rockNames) {
|
||||
for (String variant : rockVariants) {
|
||||
String fullName = name + variant;
|
||||
String id = "Test:" + fullName;
|
||||
|
||||
register(new BlockData(id));
|
||||
register(new BlockRenderOpaqueCube(id, getBlockTexture(fullName)));
|
||||
register(new BlockLogic(id));
|
||||
}
|
||||
}
|
||||
ROCKS.registerAllRocks();
|
||||
}
|
||||
|
||||
private static void registerTiles() {
|
||||
|
57
src/main/java/ru/windcorp/progressia/test/gen/RockLayer.java
Normal file
57
src/main/java/ru/windcorp/progressia/test/gen/RockLayer.java
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 ru.windcorp.progressia.common.util.noise.discrete.DiscreteNoise;
|
||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
import ru.windcorp.progressia.server.world.generation.surface.SurfaceFloatField;
|
||||
import ru.windcorp.progressia.server.world.generation.surface.TerrainLayer;
|
||||
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceBlockContext;
|
||||
|
||||
public class RockLayer implements TerrainLayer {
|
||||
|
||||
private final DiscreteNoise<TerrainLayer> strata;
|
||||
private final SurfaceFloatField depthOffsets;
|
||||
|
||||
private final double horizontalScale = 200;
|
||||
private final double verticalScale = 10;
|
||||
private final double depthInfluense = 0.1;
|
||||
|
||||
public RockLayer(DiscreteNoise<TerrainLayer> strata, SurfaceFloatField depthOffsets) {
|
||||
this.strata = strata;
|
||||
this.depthOffsets = depthOffsets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData get(SurfaceBlockContext context, float depth) {
|
||||
|
||||
double z = context.getLocation().z;
|
||||
z -= depth * depthInfluense;
|
||||
z += depthOffsets.get(context);
|
||||
z /= verticalScale;
|
||||
|
||||
return strata
|
||||
.get(
|
||||
context.getLocation().x / horizontalScale,
|
||||
context.getLocation().y / horizontalScale,
|
||||
z
|
||||
)
|
||||
.get(context, depth);
|
||||
}
|
||||
|
||||
}
|
@ -26,6 +26,7 @@ import java.util.function.Function;
|
||||
import ru.windcorp.progressia.common.Units;
|
||||
import ru.windcorp.progressia.common.util.ArrayFloatRangeMap;
|
||||
import ru.windcorp.progressia.common.util.FloatRangeMap;
|
||||
import ru.windcorp.progressia.common.util.noise.discrete.WorleyProceduralNoise;
|
||||
import ru.windcorp.progressia.common.world.Coordinates;
|
||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockDataRegistry;
|
||||
@ -36,15 +37,19 @@ import ru.windcorp.progressia.server.world.generation.planet.PlanetGenerator;
|
||||
import ru.windcorp.progressia.server.world.generation.surface.SurfaceFeature;
|
||||
import ru.windcorp.progressia.server.world.generation.surface.SurfaceFloatField;
|
||||
import ru.windcorp.progressia.server.world.generation.surface.TerrainLayer;
|
||||
import ru.windcorp.progressia.test.Rocks.RockVariant;
|
||||
import ru.windcorp.progressia.test.TestContent;
|
||||
|
||||
public class TestGenerationConfig {
|
||||
|
||||
private static final long SEED = "No bugs please".hashCode();
|
||||
|
||||
private static final float PLANET_RADIUS = Units.get("0.5 km");
|
||||
private static final float SURFACE_GRAVITY = Units.get("9.8 m/s^2");
|
||||
private static final float CURVATURE = Units.get("100 m");
|
||||
private static final float INNER_RADIUS = Units.get("200 m");
|
||||
|
||||
private static final Fields FIELDS = new Fields("No bugs please".hashCode());
|
||||
private static final Fields FIELDS = new Fields(SEED);
|
||||
|
||||
public static Function<Server, WorldGenerator> createGenerator() {
|
||||
|
||||
@ -68,31 +73,36 @@ public class TestGenerationConfig {
|
||||
}
|
||||
|
||||
private static void registerTerrainLayers(FloatRangeMap<TerrainLayer> layers) {
|
||||
BlockData granite = BlockDataRegistry.getInstance().get("Test:RedGraniteMonolith");
|
||||
BlockData graniteCracked = BlockDataRegistry.getInstance().get("Test:RedGraniteCracked");
|
||||
BlockData graniteGravel = BlockDataRegistry.getInstance().get("Test:RedGraniteGravel");
|
||||
|
||||
BlockData dirt = BlockDataRegistry.getInstance().get("Test:Dirt");
|
||||
BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
|
||||
|
||||
SurfaceFloatField cliffs = FIELDS.get("Test:CliffSelector");
|
||||
|
||||
layers.put(Float.NEGATIVE_INFINITY, 0, (f, n, w, d, r) -> air);
|
||||
layers.put(0, 4, (f, n, w, d, r) -> {
|
||||
if (cliffs.get(f, n, w) > 0) {
|
||||
switch (r.nextInt(4)) {
|
||||
case 0:
|
||||
return granite;
|
||||
case 1:
|
||||
return graniteCracked;
|
||||
default:
|
||||
return graniteGravel;
|
||||
WorleyProceduralNoise.Builder<TerrainLayer> builder = WorleyProceduralNoise.builder();
|
||||
TestContent.ROCKS.getRocks().forEach(rock -> {
|
||||
builder.add((c, d) -> {
|
||||
if (c.getRandom().nextInt(3) == 0) {
|
||||
return rock.getBlock(RockVariant.CRACKED);
|
||||
} else {
|
||||
return rock.getBlock(RockVariant.MONOLITH);
|
||||
}
|
||||
}, 1);
|
||||
});
|
||||
SurfaceFloatField rockDepthOffsets = FIELDS.register(
|
||||
"Test:RockDepthOffsets",
|
||||
() -> tweak(FIELDS.primitive(), 40, 5)
|
||||
);
|
||||
RockLayer rockLayer = new RockLayer(builder.build(SEED), rockDepthOffsets);
|
||||
|
||||
layers.put(Float.NEGATIVE_INFINITY, 0, (c, d) -> air);
|
||||
layers.put(0, 4, (c, d) -> {
|
||||
if (cliffs.get(c.getSurface().getUp(), c.getLocation().x, c.getLocation().y) > 0) {
|
||||
return rockLayer.get(c, d);
|
||||
} else {
|
||||
return dirt;
|
||||
}
|
||||
});
|
||||
layers.put(4, Float.POSITIVE_INFINITY, (f, n, w, d, r) -> granite);
|
||||
layers.put(4, Float.POSITIVE_INFINITY, rockLayer);
|
||||
}
|
||||
|
||||
private static void registerFeatures(List<SurfaceFeature> features) {
|
||||
|
Reference in New Issue
Block a user