From 9fc1a211916a816fe6a15927d6b15f53e47973d9 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Sat, 21 Aug 2021 23:05:54 +0300 Subject: [PATCH] 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 --- .../util/noise/discrete/DiscreteNoise.java | 25 ++ .../noise/discrete/WorleyProceduralNoise.java | 228 ++++++++++++++++++ .../generation/planet/PlanetGenerator.java | 2 +- .../planet/PlanetTerrainGenerator.java | 36 ++- .../world/generation/surface/Surface.java | 39 ++- .../surface/SurfaceFeatureGenerator.java | 26 +- .../surface/SurfaceTerrainGenerator.java | 51 ++-- .../generation/surface/TerrainLayer.java | 6 +- .../ru/windcorp/progressia/test/Rocks.java | 126 ++++++++++ .../windcorp/progressia/test/TestContent.java | 38 +-- .../progressia/test/gen/RockLayer.java | 57 +++++ .../test/gen/TestGenerationConfig.java | 44 ++-- 12 files changed, 574 insertions(+), 104 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/common/util/noise/discrete/DiscreteNoise.java create mode 100644 src/main/java/ru/windcorp/progressia/common/util/noise/discrete/WorleyProceduralNoise.java create mode 100644 src/main/java/ru/windcorp/progressia/test/Rocks.java create mode 100644 src/main/java/ru/windcorp/progressia/test/gen/RockLayer.java diff --git a/src/main/java/ru/windcorp/progressia/common/util/noise/discrete/DiscreteNoise.java b/src/main/java/ru/windcorp/progressia/common/util/noise/discrete/DiscreteNoise.java new file mode 100644 index 0000000..3e600f0 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/noise/discrete/DiscreteNoise.java @@ -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 . + */ +package ru.windcorp.progressia.common.util.noise.discrete; + +public interface DiscreteNoise { + + T get(double x, double y); + T get(double x, double y, double z); + +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/noise/discrete/WorleyProceduralNoise.java b/src/main/java/ru/windcorp/progressia/common/util/noise/discrete/WorleyProceduralNoise.java new file mode 100644 index 0000000..258e9b0 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/noise/discrete/WorleyProceduralNoise.java @@ -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 . + */ +package ru.windcorp.progressia.common.util.noise.discrete; + +import java.util.Collection; +import com.google.common.collect.ImmutableList; + +public class WorleyProceduralNoise implements DiscreteNoise { + + /* + * 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 { + private final T value; + private final double chance; + + public Entry(T value, double chance) { + this.value = value; + this.chance = chance; + } + } + + public static class Builder { + + com.google.common.collect.ImmutableList.Builder> builder = ImmutableList.builder(); + + public Builder add(T value, double chance) { + builder.add(new Entry<>(value, chance)); + return this; + } + + public WorleyProceduralNoise build(long seed) { + return new WorleyProceduralNoise<>(this, seed); + } + + } + + public static Builder builder() { + return new Builder<>(); + } + + private final Entry[] entries; + private final long seed; + + public WorleyProceduralNoise(Builder builder, long seed) { + this(builder.builder.build(), seed); + } + + public WorleyProceduralNoise(Collection> entries, long seed) { + this.entries = new Entry[entries.size()]; + + double chancesSum = 0; + for (Entry entry : entries) { + chancesSum += entry.chance; + } + + int i = 0; + for (Entry entry : entries) { + this.entries[i] = new Entry(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; + } + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/generation/planet/PlanetGenerator.java b/src/main/java/ru/windcorp/progressia/server/world/generation/planet/PlanetGenerator.java index b568639..b2c5c8c 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/generation/planet/PlanetGenerator.java +++ b/src/main/java/ru/windcorp/progressia/server/world/generation/planet/PlanetGenerator.java @@ -104,7 +104,7 @@ public class PlanetGenerator extends AbstractWorldGenerator { DefaultChunkData chunk = getWorldData().getChunk(chunkPos); if (chunk == null) { - chunk = terrainGenerator.generateTerrain(chunkPos); + chunk = terrainGenerator.generateTerrain(getServer(), chunkPos); getWorldData().addChunk(chunk); } } diff --git a/src/main/java/ru/windcorp/progressia/server/world/generation/planet/PlanetTerrainGenerator.java b/src/main/java/ru/windcorp/progressia/server/world/generation/planet/PlanetTerrainGenerator.java index ab081db..dba3b8a 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/generation/planet/PlanetTerrainGenerator.java +++ b/src/main/java/ru/windcorp/progressia/server/world/generation/planet/PlanetTerrainGenerator.java @@ -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 surfaceGenerators; - public PlanetTerrainGenerator(PlanetGenerator generator, SurfaceFloatField heightMap, FloatRangeMap layers) { + public PlanetTerrainGenerator( + PlanetGenerator generator, + SurfaceFloatField heightMap, + FloatRangeMap 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"); diff --git a/src/main/java/ru/windcorp/progressia/server/world/generation/surface/Surface.java b/src/main/java/ru/windcorp/progressia/server/world/generation/surface/Surface.java index 296e3eb..08c9308 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/generation/surface/Surface.java +++ b/src/main/java/ru/windcorp/progressia/server/world/generation/surface/Surface.java @@ -17,18 +17,28 @@ */ 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 { - + private final AbsFace up; private final int seaLevel; - + public Surface(AbsFace up, int seaLevel) { this.up = up; this.seaLevel = seaLevel; } - + /** * @return the up */ @@ -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; + + } + } diff --git a/src/main/java/ru/windcorp/progressia/server/world/generation/surface/SurfaceFeatureGenerator.java b/src/main/java/ru/windcorp/progressia/server/world/generation/surface/SurfaceFeatureGenerator.java index abfbcb3..3548184 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/generation/surface/SurfaceFeatureGenerator.java +++ b/src/main/java/ru/windcorp/progressia/server/world/generation/surface/SurfaceFeatureGenerator.java @@ -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); diff --git a/src/main/java/ru/windcorp/progressia/server/world/generation/surface/SurfaceTerrainGenerator.java b/src/main/java/ru/windcorp/progressia/server/world/generation/surface/SurfaceTerrainGenerator.java index a3511bd..35b4a58 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/generation/surface/SurfaceTerrainGenerator.java +++ b/src/main/java/ru/windcorp/progressia/server/world/generation/surface/SurfaceTerrainGenerator.java @@ -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 layers; - public SurfaceTerrainGenerator(SurfaceFloatField heightMap, FloatRangeMap layers) { + public SurfaceTerrainGenerator(Surface surface, SurfaceFloatField heightMap, FloatRangeMap 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) { - - float north = relBIC.x + offset.x; - float west = relBIC.y + offset.y; - - float relSurface = heightMap.get(chunk.getUp(), north, west) - offset.z; - + + public void generateColumn(DefaultChunkData chunk, Vec3i relBIC, Vec3 offset, SurfaceWorldContext context) { + + int north = (int) (relBIC.x + offset.x); + int west = (int) (relBIC.y + offset.y); + + 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); + } } diff --git a/src/main/java/ru/windcorp/progressia/server/world/generation/surface/TerrainLayer.java b/src/main/java/ru/windcorp/progressia/server/world/generation/surface/TerrainLayer.java index 7af46fa..5ec131c 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/generation/surface/TerrainLayer.java +++ b/src/main/java/ru/windcorp/progressia/server/world/generation/surface/TerrainLayer.java @@ -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); } diff --git a/src/main/java/ru/windcorp/progressia/test/Rocks.java b/src/main/java/ru/windcorp/progressia/test/Rocks.java new file mode 100644 index 0000000..ebf31fe --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/test/Rocks.java @@ -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 . + */ +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 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 rocksByName = Collections.synchronizedMap(new HashMap<>()); + private final Multimap 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 getRocks() { + return rocksByName.values(); + } + + public Collection getRocks(RockType type) { + return rocksByType.get(type); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index 9e868b4..1244560 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java @@ -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 { @@ -69,6 +68,8 @@ public class TestContent { public static final List PLACEABLE_BLOCKS = new ArrayList<>(); public static final List PLACEABLE_TILES = new ArrayList<>(); + + public static final Rocks ROCKS = new Rocks(); public static void registerContent() { registerWorldContent(); @@ -150,33 +151,16 @@ public class TestContent { } private static void registerRocks() { - List rockNames = ImmutableList.of( - "BlackGranite", - "Dolomite", - "Eclogite", - "Gabbro", - "Limestone", - "Marble", - "RedGranite" - ); - List 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() { diff --git a/src/main/java/ru/windcorp/progressia/test/gen/RockLayer.java b/src/main/java/ru/windcorp/progressia/test/gen/RockLayer.java new file mode 100644 index 0000000..5b41ef1 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/test/gen/RockLayer.java @@ -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 . + */ +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 strata; + private final SurfaceFloatField depthOffsets; + + private final double horizontalScale = 200; + private final double verticalScale = 10; + private final double depthInfluense = 0.1; + + public RockLayer(DiscreteNoise 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); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/test/gen/TestGenerationConfig.java b/src/main/java/ru/windcorp/progressia/test/gen/TestGenerationConfig.java index 22a66d7..cc1ff8b 100644 --- a/src/main/java/ru/windcorp/progressia/test/gen/TestGenerationConfig.java +++ b/src/main/java/ru/windcorp/progressia/test/gen/TestGenerationConfig.java @@ -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 createGenerator() { @@ -68,31 +73,36 @@ public class TestGenerationConfig { } private static void registerTerrainLayers(FloatRangeMap 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 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 features) { @@ -101,7 +111,7 @@ public class TestGenerationConfig { "Test:Forestiness", () -> squash(scale(FIELDS.primitive(), 200), 5) ); - + SurfaceFloatField floweriness = FIELDS.register( "Test:Floweriness", f -> multiply(