Replaced placeholder worldgen with a passable one

- Removed old (pre-planet) worldgen
  - TestGravityModel remains
- Moved surface generator to .logic.world.generation.surface
- Split planet generator into generator logic and config
  - Logic moved to .logic.world.generation.planet
  - Config extracted into TestGenerationConfig & others in .test.gen
  - GravityModel renamed to Test:PlanetGravityModel
- TestTerrainGenerator utilities moved and made public thru Fields
- Reconfigured planet generator to be a passable
  - Increased planet size to R=0.5 km
  - Added noise-based heightmaps (fabulous cliffs included)
  - Added noise-based forest density map
  - Reworked all SurfaceFeatures
    - TestGrassFeature now also places scatter and flowers
    - TestTreeFeature and TestBushFeature:
      - Common code exctracted to MultiblockVegetationFeature
      - Made prettier
  - gud muscle flex yeeeeeeeeeeee
- Fixed a bug in the gravity model
- A lot of other changes that I already forgot about
This commit is contained in:
OLEGSHA 2021-08-20 18:07:41 +03:00
parent 15b5d367b4
commit 2328f2ae3a
Signed by: OLEGSHA
GPG Key ID: E57A4B08D64AFF7A
39 changed files with 1032 additions and 1146 deletions

View File

@ -19,6 +19,7 @@
package ru.windcorp.progressia.server;
import java.util.function.Consumer;
import java.util.function.Function;
import org.apache.logging.log4j.LogManager;
@ -44,12 +45,11 @@ import ru.windcorp.progressia.server.world.context.ServerWorldContext;
import ru.windcorp.progressia.server.world.context.impl.DefaultServerContext;
import ru.windcorp.progressia.server.world.context.impl.ReportingServerContext;
import ru.windcorp.progressia.server.world.context.impl.RotatingServerContext;
import ru.windcorp.progressia.server.world.generation.WorldGenerator;
import ru.windcorp.progressia.server.world.tasks.WorldAccessor;
import ru.windcorp.progressia.server.world.ticking.Change;
import ru.windcorp.progressia.server.world.ticking.Evaluation;
import ru.windcorp.progressia.server.world.ticking.TickerCoordinator;
import ru.windcorp.progressia.test.gen.planet.Planet;
import ru.windcorp.progressia.test.gen.planet.TestPlanetGenerator;
public class Server {
@ -78,11 +78,11 @@ public class Server {
private final TickingSettings tickingSettings = new TickingSettings();
public Server(DefaultWorldData world) {
public Server(DefaultWorldData world, Function<Server, WorldGenerator> generatorCreator) {
this.world = new DefaultWorldLogic(
world,
this,
new TestPlanetGenerator("Test:PlanetGenerator", this, new Planet(4, 9.8f, 16f, 16f)),
generatorCreator.apply(this),
worldAccessor
);
this.serverThread = new ServerThread(this);

View File

@ -19,6 +19,7 @@
package ru.windcorp.progressia.server;
import ru.windcorp.progressia.common.world.DefaultWorldData;
import ru.windcorp.progressia.test.gen.TestGenerationConfig;
public class ServerState {
@ -33,7 +34,7 @@ public class ServerState {
}
public static void startServer() {
Server server = new Server(new DefaultWorldData());
Server server = new Server(new DefaultWorldData(), TestGenerationConfig.createGenerator());
setInstance(server);
server.start();
}

View File

@ -18,8 +18,8 @@
package ru.windcorp.progressia.server.world.context.impl;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.generic.GenericChunks;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.common.world.rels.AxisRotations;
import ru.windcorp.progressia.common.world.rels.RelFace;
import ru.windcorp.progressia.server.world.context.ServerTileContext;
@ -38,12 +38,12 @@ public class RotatingServerContext extends TransformingServerContext {
@Override
protected void transform(Vec3i userLocation, Vec3i output) {
AxisRotations.resolve(userLocation, up, output);
GenericChunks.resolve(userLocation, up, output);
}
@Override
protected void untransform(Vec3i parentLocation, Vec3i output) {
AxisRotations.relativize(parentLocation, up, output);
GenericChunks.relativize(parentLocation, up, output);
}
@Override

View File

@ -15,7 +15,7 @@
* 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;
package ru.windcorp.progressia.server.world.generation.planet;
import ru.windcorp.progressia.common.world.DefaultChunkData;
@ -23,7 +23,7 @@ public class Planet {
private final int radiusInChunks;
private final TestPlanetGravityModel.Settings gravityModelSettings;
private final PlanetGravityModel.Settings gravityModelSettings;
public Planet(
int radiusInChunks,
@ -32,7 +32,7 @@ public class Planet {
float innerGravityRadius
) {
this.radiusInChunks = radiusInChunks;
this.gravityModelSettings = new TestPlanetGravityModel.Settings(
this.gravityModelSettings = new PlanetGravityModel.Settings(
surfaceGravitationalAcceleration,
curvature,
innerGravityRadius
@ -82,7 +82,7 @@ public class Planet {
/**
* @return the gravityModelSettings
*/
public TestPlanetGravityModel.Settings getGravityModelSettings() {
public PlanetGravityModel.Settings getGravityModelSettings() {
return gravityModelSettings;
}

View File

@ -15,10 +15,9 @@
* 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;
package ru.windcorp.progressia.server.world.generation.planet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import glm.vec._3.i.Vec3i;
@ -26,27 +25,19 @@ import ru.windcorp.progressia.common.util.VectorUtil;
import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.test.TestBushFeature;
import ru.windcorp.progressia.test.TestGrassFeature;
import ru.windcorp.progressia.test.TestTreeFeature;
import ru.windcorp.progressia.test.gen.surface.Surface;
import ru.windcorp.progressia.test.gen.surface.SurfaceFeature;
import ru.windcorp.progressia.test.gen.surface.SurfaceFeatureGenerator;
import ru.windcorp.progressia.server.world.generation.surface.Surface;
import ru.windcorp.progressia.server.world.generation.surface.SurfaceFeature;
import ru.windcorp.progressia.server.world.generation.surface.SurfaceFeatureGenerator;
public class PlanetFeatureGenerator {
private final TestPlanetGenerator parent;
private final PlanetGenerator parent;
private final Map<AbsFace, SurfaceFeatureGenerator> surfaceGenerators;
public PlanetFeatureGenerator(TestPlanetGenerator generator) {
public PlanetFeatureGenerator(PlanetGenerator generator, List<SurfaceFeature> features) {
this.parent = generator;
Collection<SurfaceFeature> features = new ArrayList<>();
features.add(new TestBushFeature("Test:BushFeature"));
features.add(new TestTreeFeature("Test:TreeFeature"));
features.add(new TestGrassFeature("Test:GrassFeature"));
int seaLevel = (int) parent.getPlanet().getRadius();
this.surfaceGenerators = AbsFace.mapToFaces(face -> new SurfaceFeatureGenerator(
new Surface(face, seaLevel),
@ -54,7 +45,7 @@ public class PlanetFeatureGenerator {
));
}
public TestPlanetGenerator getGenerator() {
public PlanetGenerator getGenerator() {
return parent;
}

View File

@ -15,37 +15,49 @@
* 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;
package ru.windcorp.progressia.server.world.generation.planet;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.List;
import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.util.FloatRangeMap;
import ru.windcorp.progressia.common.util.VectorUtil;
import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.DecodingException;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.world.generation.AbstractWorldGenerator;
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;
public class TestPlanetGenerator extends AbstractWorldGenerator<Boolean> {
public class PlanetGenerator extends AbstractWorldGenerator<Boolean> {
private final Planet planet;
private final PlanetTerrainGenerator terrainGenerator;
private final PlanetFeatureGenerator featureGenerator;
public TestPlanetGenerator(String id, Server server, Planet planet) {
public PlanetGenerator(
String id,
Server server,
Planet planet,
SurfaceFloatField heightMap,
FloatRangeMap<TerrainLayer> layers,
List<SurfaceFeature> features
) {
super(id, server, Boolean.class, "Test:PlanetGravityModel");
this.planet = planet;
TestPlanetGravityModel model = (TestPlanetGravityModel) this.getGravityModel();
PlanetGravityModel model = (PlanetGravityModel) this.getGravityModel();
model.configure(planet.getGravityModelSettings());
this.terrainGenerator = new PlanetTerrainGenerator(this);
this.featureGenerator = new PlanetFeatureGenerator(this);
this.terrainGenerator = new PlanetTerrainGenerator(this, heightMap, layers);
this.featureGenerator = new PlanetFeatureGenerator(this, features);
}
/**
@ -57,7 +69,7 @@ public class TestPlanetGenerator extends AbstractWorldGenerator<Boolean> {
@Override
public Vec3 suggestSpawnLocation() {
return new Vec3(7f, 7f, getPlanet().getRadius() + 10);
return new Vec3(407f, 1f, getPlanet().getRadius() + 10);
}
@Override

View File

@ -15,7 +15,7 @@
* 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;
package ru.windcorp.progressia.server.world.generation.planet;
import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i;
@ -30,7 +30,7 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class TestPlanetGravityModel extends GravityModel {
public class PlanetGravityModel extends GravityModel {
public static class Settings {
public float surfaceGravitationalAcceleration;
@ -66,7 +66,7 @@ public class TestPlanetGravityModel extends GravityModel {
private Settings settings = new Settings();
public TestPlanetGravityModel(String id) {
public PlanetGravityModel(String id) {
super(id);
}
@ -90,7 +90,6 @@ public class TestPlanetGravityModel extends GravityModel {
protected void doGetGravity(Vec3 pos, Vec3 output) {
float r = getInnerRadius();
float c = getCurvature();
float g = getSurfaceGravitationalAcceleration();
// Change to a CS where (0;0;0) is the center of the center chunk
float px = pos.x - DefaultChunkData.CHUNK_RADIUS + 0.5f;
@ -142,14 +141,14 @@ public class TestPlanetGravityModel extends GravityModel {
assert output.dot(output) == 1 : "maxAbs - midAbs = " + maxAbs + " - " + midAbs + " > " + c + " yet l*l != 1";
}
output.mul(-g);
output.mul(-getSurfaceGravitationalAcceleration());
}
private void computeEdgeGravity(float lx, float ly, float lz, float rx, float ry, float rz, Vec3 output) {
// da math is gud, no worry
// - Javapony
float r = getInnerRadius();
float c = getCurvature();
if (lx == 0) rx = 0;
if (ly == 0) ry = 0;
@ -159,10 +158,10 @@ public class TestPlanetGravityModel extends GravityModel {
float rSquared = rx*rx + ry*ry + rz*rz;
float distanceAlongEdge = scalarProduct - (float) sqrt(
scalarProduct*scalarProduct - rSquared + r*r
scalarProduct*scalarProduct - rSquared + c*c
);
output.set(lx, ly, lz).mul(-distanceAlongEdge).add(rx, ry, rz).div(r);
output.set(lx, ly, lz).mul(-distanceAlongEdge).add(rx, ry, rz).div(c);
final float f = (float) sqrt(3.0/2);

View File

@ -15,11 +15,10 @@
* 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;
package ru.windcorp.progressia.server.world.generation.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.DefaultChunkData;
@ -27,37 +26,22 @@ 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.test.gen.TerrainLayer;
import ru.windcorp.progressia.test.gen.surface.SurfaceFloatField;
import ru.windcorp.progressia.test.gen.surface.SurfaceTerrainGenerator;
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;
class PlanetTerrainGenerator {
private final TestPlanetGenerator parent;
private final PlanetGenerator parent;
private final SurfaceTerrainGenerator surfaceGenerator;
public PlanetTerrainGenerator(TestPlanetGenerator generator) {
public PlanetTerrainGenerator(PlanetGenerator generator, SurfaceFloatField heightMap, FloatRangeMap<TerrainLayer> layers) {
this.parent = generator;
SurfaceFloatField heightMap = new TestHeightMap(
generator.getPlanet().getRadius() - DefaultChunkData.BLOCKS_PER_CHUNK,
generator.getPlanet().getRadius() / 4,
5,
6
);
FloatRangeMap<TerrainLayer> layers = new ArrayFloatRangeMap<>();
BlockData granite = BlockDataRegistry.getInstance().get("Test:GraniteMonolith");
BlockData dirt = BlockDataRegistry.getInstance().get("Test:Dirt");
BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
layers.put(Float.NEGATIVE_INFINITY, 0, (n, w, d, r, c) -> air);
layers.put(0, 4, (n, w, d, r, c) -> dirt);
layers.put(4, 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);
SurfaceFloatField adjustedHeightMap = (f, n, w) -> heightMap.get(f, n, w) + generator.getPlanet().getRadius();
this.surfaceGenerator = new SurfaceTerrainGenerator(adjustedHeightMap, layers);
}
public TestPlanetGenerator getGenerator() {
public PlanetGenerator getGenerator() {
return parent;
}

View File

@ -15,7 +15,7 @@
* 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;
package ru.windcorp.progressia.server.world.generation.surface;
import ru.windcorp.progressia.common.world.rels.AbsFace;

View File

@ -15,10 +15,13 @@
* 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;
package ru.windcorp.progressia.server.world.generation.surface;
import java.util.List;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.test.gen.surface.context.SurfaceWorldContext;
import ru.windcorp.progressia.server.world.context.ServerContext;
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceWorldContext;
public abstract class SurfaceFeature extends Namespaced {
@ -28,4 +31,16 @@ public abstract class SurfaceFeature extends Namespaced {
public abstract void process(SurfaceWorldContext context);
protected static double randomDouble(ServerContext context, double from, double to) {
return from + (to - from) * context.getRandom().nextDouble();
}
protected static double stretch(double t, double from, double to) {
return from + (to - from) * t;
}
protected static <T> T pickRandom(ServerContext context, List<T> from) {
return from.get(context.getRandom().nextInt(from.size()));
}
}

View File

@ -15,10 +15,10 @@
* 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;
package ru.windcorp.progressia.server.world.generation.surface;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import glm.Glm;
@ -27,15 +27,15 @@ 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.test.gen.surface.context.SurfaceContextImpl;
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceContextImpl;
public class SurfaceFeatureGenerator {
private final Surface surface;
private final Collection<SurfaceFeature> features; // TODO make ordered
private final List<SurfaceFeature> features;
public SurfaceFeatureGenerator(Surface surface, Collection<SurfaceFeature> features) {
public SurfaceFeatureGenerator(Surface surface, List<SurfaceFeature> features) {
this.surface = surface;
this.features = new ArrayList<>(features);
}

View File

@ -15,13 +15,20 @@
* 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;
package ru.windcorp.progressia.server.world.generation.surface;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceBlockContext;
@FunctionalInterface
public interface SurfaceFloatField {
float get(AbsFace face, float north, float west);
float get(AbsFace face, float x, float y);
default float get(SurfaceBlockContext context) {
Vec3i location = context.getLocation();
return get(context.getSurface().getUp(), location.x, location.y);
}
}

View File

@ -15,7 +15,7 @@
* 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;
package ru.windcorp.progressia.server.world.generation.surface;
import java.util.Random;
@ -26,7 +26,6 @@ import ru.windcorp.progressia.common.util.FloatRangeMap;
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.test.gen.TerrainLayer;
public class SurfaceTerrainGenerator {
@ -44,7 +43,7 @@ public class SurfaceTerrainGenerator {
Vec3 offset = new Vec3(chunk.getMinX(), chunk.getMinY(), chunk.getMinZ());
AxisRotations.relativize(offset, chunk.getUp(), offset);
offset.sub(DefaultChunkData.CHUNK_RADIUS - 0.5f);
offset.z -= DefaultChunkData.CHUNK_RADIUS - 0.5f;
Random random = new Random(CoordinatePacker.pack3IntsIntoLong(chunk.getPosition()) /* ^ seed*/);
@ -65,7 +64,7 @@ public class SurfaceTerrainGenerator {
for (relBIC.z = 0; relBIC.z < DefaultChunkData.BLOCKS_PER_CHUNK; ++relBIC.z) {
float depth = relSurface - relBIC.z;
BlockData block = layers.get(depth).get(north, west, depth, random, chunk);
BlockData block = layers.get(depth).get(chunk.getUp(), north, west, depth, random);
chunk.resolve(relBIC, relBIC);
chunk.setBlock(relBIC, block, false);

View File

@ -15,11 +15,11 @@
* 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;
package ru.windcorp.progressia.server.world.generation.surface;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.test.gen.surface.context.SurfaceBlockContext;
import ru.windcorp.progressia.test.gen.surface.context.SurfaceWorldContext;
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceBlockContext;
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceWorldContext;
public abstract class SurfaceTopLayerFeature extends SurfaceFeature {

View File

@ -15,16 +15,16 @@
* 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;
package ru.windcorp.progressia.server.world.generation.surface;
import java.util.Random;
import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.block.BlockData;
import ru.windcorp.progressia.common.world.rels.AbsFace;
@FunctionalInterface
public interface TerrainLayer {
BlockData get(float north, float west, float depth, Random random, DefaultChunkData chunk);
BlockData get(AbsFace face, float north, float west, float depth, Random random);
}

View File

@ -15,7 +15,7 @@
* 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.context;
package ru.windcorp.progressia.server.world.generation.surface.context;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.rels.RelFace;

View File

@ -15,13 +15,13 @@
* 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.context;
package ru.windcorp.progressia.server.world.generation.surface.context;
import java.util.function.Consumer;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.util.VectorUtil;
import ru.windcorp.progressia.test.gen.surface.Surface;
import ru.windcorp.progressia.server.world.generation.surface.Surface;
public interface SurfaceContext {

View File

@ -15,7 +15,7 @@
* 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.context;
package ru.windcorp.progressia.server.world.generation.surface.context;
import java.util.Random;
@ -23,7 +23,7 @@ import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.rels.RelFace;
import ru.windcorp.progressia.server.world.context.ServerTileContext;
import ru.windcorp.progressia.server.world.context.impl.RotatingServerContext;
import ru.windcorp.progressia.test.gen.surface.Surface;
import ru.windcorp.progressia.server.world.generation.surface.Surface;
public class SurfaceContextImpl extends RotatingServerContext implements SurfaceTileContext {

View File

@ -15,12 +15,12 @@
* 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.context;
package ru.windcorp.progressia.server.world.generation.surface.context;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.rels.RelFace;
import ru.windcorp.progressia.server.world.context.impl.DefaultServerContextLogic;
import ru.windcorp.progressia.test.gen.surface.Surface;
import ru.windcorp.progressia.server.world.generation.surface.Surface;
public class SurfaceContextImplLogic extends DefaultServerContextLogic implements SurfaceTileContext.Logic {

View File

@ -15,7 +15,7 @@
* 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.context;
package ru.windcorp.progressia.server.world.generation.surface.context;
import ru.windcorp.progressia.server.world.context.ServerTileContext;

View File

@ -15,7 +15,7 @@
* 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.context;
package ru.windcorp.progressia.server.world.generation.surface.context;
import ru.windcorp.progressia.server.world.context.ServerTileStackContext;

View File

@ -15,7 +15,7 @@
* 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.context;
package ru.windcorp.progressia.server.world.generation.surface.context;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.rels.RelFace;

View File

@ -1,66 +0,0 @@
/*
* 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 glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.util.VectorUtil;
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.test.gen.surface.SurfaceTopLayerFeature;
import ru.windcorp.progressia.test.gen.surface.context.SurfaceBlockContext;
import ru.windcorp.progressia.test.gen.surface.context.SurfaceWorldContext;
public class TestBushFeature extends SurfaceTopLayerFeature {
public TestBushFeature(String id) {
super(id);
}
private void tryToSetLeaves(SurfaceWorldContext context, Vec3i location, BlockData leaves) {
if (context.getBlock(location).getId().equals("Test:Air")) {
context.setBlock(location, leaves);
}
}
@Override
protected void processTopBlock(SurfaceBlockContext context) {
if (context.getRandom().nextInt(10*10) > 0) return;
Vec3i center = context.getLocation().add_(0, 0, 1);
BlockData log = BlockDataRegistry.getInstance().get("Test:Log");
BlockData leaves = BlockDataRegistry.getInstance().get("Test:TemporaryLeaves");
context.setBlock(center, log);
VectorUtil.iterateCuboidAround(center.x, center.y, center.z, 3, 3, 3, p -> {
tryToSetLeaves(context, p, leaves);
});
VectorUtil.iterateCuboidAround(center.x, center.y, center.z, 5, 5, 1, p -> {
tryToSetLeaves(context, p, leaves);
});
}
@Override
protected boolean isSolid(SurfaceBlockContext context) {
return context.logic().getBlock().isSolid(RelFace.UP);
}
}

View File

@ -27,7 +27,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import org.lwjgl.glfw.GLFW;
import glm.vec._3.i.Vec3i;
@ -56,9 +55,9 @@ import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.comms.controls.*;
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.gen.TestGravityModel;
import ru.windcorp.progressia.test.gen.planet.TestPlanetGravityModel;
public class TestContent {
@ -435,7 +434,7 @@ public class TestContent {
ChunkIO.registerCodec(new TestChunkCodec());
ChunkRenderOptimizerRegistry.getInstance().register("Core:SurfaceOptimizer", ChunkRenderOptimizerSurface::new);
GravityModelRegistry.getInstance().register("Test:TheGravityModel", TestGravityModel::new);
GravityModelRegistry.getInstance().register("Test:PlanetGravityModel", TestPlanetGravityModel::new);
GravityModelRegistry.getInstance().register("Test:PlanetGravityModel", PlanetGravityModel::new);
}
}

View File

@ -1,70 +0,0 @@
/*
* 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.Set;
import com.google.common.collect.ImmutableSet;
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.test.gen.surface.SurfaceTopLayerFeature;
import ru.windcorp.progressia.test.gen.surface.context.SurfaceBlockContext;
public class TestGrassFeature extends SurfaceTopLayerFeature {
private static final Set<String> WHITELIST = ImmutableSet.of(
"Test:Dirt",
"Test:Stone",
"Test:GraniteMonolith",
"Test:GraniteCracked",
"Test:GraniteGravel"
);
public TestGrassFeature(String id) {
super(id);
}
@Override
protected void processTopBlock(SurfaceBlockContext context) {
if (!WHITELIST.contains(context.getBlock().getId())) {
return;
}
TileData grass = TileDataRegistry.getInstance().get("Test:Grass");
for (RelFace face : RelFace.getFaces()) {
if (face == RelFace.DOWN) continue;
if (context.pushRelative(face).logic().getBlock().isTransparent()) {
context.pop();
context.addTile(face, grass);
} else {
context.pop();
}
}
}
@Override
protected boolean isSolid(SurfaceBlockContext context) {
return context.logic().getBlock().isSolid(RelFace.UP);
}
}

View File

@ -1,106 +0,0 @@
/*
* 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.function.Consumer;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.util.VectorUtil;
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.test.gen.surface.SurfaceTopLayerFeature;
import ru.windcorp.progressia.test.gen.surface.context.SurfaceBlockContext;
import ru.windcorp.progressia.test.gen.surface.context.SurfaceWorldContext;
public class TestTreeFeature extends SurfaceTopLayerFeature {
public TestTreeFeature(String id) {
super(id);
}
private void tryToSetLeaves(SurfaceWorldContext context, Vec3i location, BlockData leaves) {
if (context.getBlock(location).getId().equals("Test:Air")) {
context.setBlock(location, leaves);
}
}
private void iterateSpheroid(Vec3i center, double horDiameter, double vertDiameter, Consumer<Vec3i> action) {
VectorUtil.iterateCuboidAround(
center.x,
center.y,
center.z,
(int) Math.ceil(horDiameter) / 2 * 2 + 5,
(int) Math.ceil(horDiameter) / 2 * 2 + 5,
(int) Math.ceil(vertDiameter) / 2 * 2 + 5,
pos -> {
double sx = (pos.x - center.x) / horDiameter;
double sy = (pos.y - center.y) / horDiameter;
double sz = (pos.z - center.z) / vertDiameter;
if (sx * sx + sy * sy + sz * sz <= 1) {
action.accept(pos);
}
}
);
}
@Override
protected void processTopBlock(SurfaceBlockContext context) {
if (context.getRandom().nextInt(20 * 20) > 0)
return;
Vec3i start = context.getLocation().add_(0, 0, 1);
BlockData log = BlockDataRegistry.getInstance().get("Test:Log");
BlockData leaves = BlockDataRegistry.getInstance().get("Test:TemporaryLeaves");
Vec3i center = start.add_(0);
int height = context.getRandom().nextInt(3) + 5;
for (; center.z < start.z + height; ++center.z) {
context.setBlock(center, log);
}
double branchHorDistance = 0;
do {
double branchSize = 0.5 + 1 * context.getRandom().nextDouble();
double branchHorAngle = 2 * Math.PI * context.getRandom().nextDouble();
int branchVertOffset = -2 + context.getRandom().nextInt(3);
Vec3i branchCenter = center.add_(
(int) (Math.sin(branchHorAngle) * branchHorDistance),
(int) (Math.cos(branchHorAngle) * branchHorDistance),
branchVertOffset
);
iterateSpheroid(branchCenter, 1.75 * branchSize, 2.5 * branchSize, p -> {
tryToSetLeaves(context, p, leaves);
});
branchHorDistance = 1 + 2 * context.getRandom().nextDouble();
} while (context.getRandom().nextInt(8) > 1);
}
@Override
protected boolean isSolid(SurfaceBlockContext context) {
return context.logic().getBlock().isSolid(RelFace.UP);
}
}

View File

@ -0,0 +1,321 @@
/*
* 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.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.function.Function;
import java.util.function.Supplier;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import kdotjpg.opensimplex2.areagen.OpenSimplex2S;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.Coordinates;
import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.server.world.generation.planet.Planet;
import ru.windcorp.progressia.server.world.generation.surface.SurfaceFloatField;
public class Fields {
@FunctionalInterface
public interface Field {
double compute(double x, double y);
}
public static class FieldSet extends Namespaced implements SurfaceFloatField {
private final Field[] fields = new Field[AbsFace.getFaces().size()];
public FieldSet(String id) {
super(id);
}
public void put(AbsFace face, Field field) {
fields[face.getId()] = field;
}
public Field get(AbsFace face) {
return fields[face.getId()];
}
@Override
public float get(AbsFace face, float x, float y) {
float offset = DefaultChunkData.CHUNK_RADIUS - 0.5f;
return (float) fields[face.getId()].compute(x - offset, y - offset);
}
}
private final Random primitiveRandomizer;
private final OpenSimplex2S noise;
private final Map<String, FieldSet> registeredFields = Collections.synchronizedMap(new HashMap<>());
private final Logger logger = LogManager.getLogger(getClass());
public Fields(long seed) {
this.primitiveRandomizer = new Random(seed);
this.noise = new OpenSimplex2S(seed);
}
public Field register(String id, AbsFace face, Field f) {
Objects.requireNonNull(f, "f");
Objects.requireNonNull(face, "face");
synchronized (registeredFields) {
FieldSet fieldSet = registeredFields.computeIfAbsent(id, FieldSet::new);
Field previous = fieldSet.get(face);
if (previous != null) {
throw new IllegalArgumentException(
"Duplicate field definition " + id + ":" + face + " for fields " + f + " and " + previous
);
}
fieldSet.put(face, f);
logger.debug("Registering {}:{} in {}", id, face, getClass().getSimpleName());
}
return f;
}
public SurfaceFloatField register(String id, Function<AbsFace, Field> fieldGenerator) {
for (AbsFace face : AbsFace.getFaces()) {
register(id, face, fieldGenerator.apply(face));
}
return get(id);
}
public SurfaceFloatField register(String id, Supplier<Field> fieldGenerator) {
for (AbsFace face : AbsFace.getFaces()) {
register(id, face, fieldGenerator.get());
}
return get(id);
}
public SurfaceFloatField get(String id) {
return registeredFields.get(id);
}
public Field get(String id, AbsFace face) {
return registeredFields.get(id).get(face);
}
public static Field cutoff(Field f, double outerRadius, double thickness) {
return (x, y) -> {
double cutoffCoefficient = 1;
cutoffCoefficient *= cutoffFunction(outerRadius - x, thickness);
cutoffCoefficient *= cutoffFunction(outerRadius + x, thickness);
cutoffCoefficient *= cutoffFunction(outerRadius - y, thickness);
cutoffCoefficient *= cutoffFunction(outerRadius + y, thickness);
if (cutoffCoefficient == 0) {
return 0;
}
return cutoffCoefficient * f.compute(x, y);
};
}
public static Field cutoff(Field f, Planet planet, double thickness) {
return cutoff(f, planet.getRadius() - Coordinates.CHUNK_SIZE, thickness);
}
private static double cutoffFunction(double distanceToCutoffPoint, double thickness) {
if (distanceToCutoffPoint < 0) {
return 0;
} else if (distanceToCutoffPoint < thickness) {
double t = distanceToCutoffPoint / thickness;
return (1 - Math.cos(Math.PI * t)) / 2;
} else {
return 1;
}
}
public Field primitive() {
double xOffset = primitiveRandomizer.nextDouble() * 200 - 100;
double yOffset = primitiveRandomizer.nextDouble() * 200 - 100;
double rotation = primitiveRandomizer.nextDouble() * 2 * Math.PI;
double sin = Math.sin(rotation);
double cos = Math.cos(rotation);
return (x, y) -> noise.noise2(x * cos - y * sin + xOffset, x * sin + y * cos + yOffset);
}
public static Field add(Field a, Field b) {
return (x, y) -> a.compute(x, y) + b.compute(x, y);
}
public static Field multiply(Field a, Field b) {
return (x, y) -> a.compute(x, y) * b.compute(x, y);
}
public static Field add(Field... functions) {
return (x, y) -> {
double sum = 0;
for (Field function : functions) {
sum += function.compute(x, y);
}
return sum;
};
}
public static Field multiply(Field... functions) {
return (x, y) -> {
double product = 1;
for (Field function : functions) {
product *= function.compute(x, y);
}
return product;
};
}
public static Field tweak(Field f, double scale, double amplitude, double bias) {
return (x, y) -> f.compute(x / scale, y / scale) * amplitude + bias;
}
public static Field tweak(Field f, double scale, double amplitude) {
return tweak(f, scale, amplitude, 0);
}
public static Field scale(Field f, double scale) {
return tweak(f, scale, 1, 0);
}
public static Field amplify(Field f, double amplitude) {
return tweak(f, 1, amplitude, 0);
}
public static Field bias(Field f, double bias) {
return tweak(f, 1, 1, bias);
}
public static Field octaves(Field f, double scaleFactor, double amplitudeFactor, int octaves) {
return (x, y) -> {
double result = 0;
double scale = 1;
double amplitude = 1;
for (int i = 0; i < octaves; ++i) {
result += f.compute(x * scale, y * scale) * amplitude;
scale *= scaleFactor;
amplitude /= amplitudeFactor;
}
return result;
};
}
public static Field octaves(Field f, double factor, int octaves) {
return octaves(f, factor, factor, octaves);
}
public static Field squash(Field f, double slope) {
return (x, y) -> 1 / (1 + Math.exp(-slope * f.compute(x, y)));
}
public static Field ridge(Field f) {
return (x, y) -> 1 - Math.abs(f.compute(x, y));
}
public static Field clamp(Field f, double min, double max) {
return (x, y) -> Math.min(Math.max(f.compute(x, y), min), max);
}
public static Field withMin(Field f, double min) {
return (x, y) -> Math.max(f.compute(x, y), min);
}
public static Field withMax(Field f, double max) {
return (x, y) -> Math.min(f.compute(x, y), max);
}
public static Field select(Field f, double target, double width) {
return (x, y) -> {
double value = f.compute(x, y);
if (value < target - width) {
return 0;
} else if (value < target) {
return (width - (target - value)) / width;
} else if (value < target + width) {
return (width - (value - target)) / width;
} else {
return 0;
}
};
}
public static Field selectPositive(Field f, double target, double width) {
return (x, y) -> {
double value = f.compute(x, y);
if (value < target - width) {
return 0;
} else if (value < target + width) {
return (width - (target - value)) / (2*width);
} else {
return 1;
}
};
}
public static Field selectNegative(Field f, double target, double width) {
return (x, y) -> {
double value = target - f.compute(x, y);
if (value < target - width) {
return 0;
} else if (value < target + width) {
return (width - (target - value)) / (2*width);
} else {
return 1;
}
};
}
public static Field cliff(Field f, double target, double featureWidth, double slopeWidth) {
return (x, y) -> {
double value = f.compute(x, y);
if (value < target - featureWidth) {
return 0;
} else if (value < target - slopeWidth) {
double t = (value - (target - featureWidth)) / (featureWidth - slopeWidth);
return -0.5 + Math.cos(t * Math.PI) / 2;
} else if (value < target + slopeWidth) {
double t = (value - (target - slopeWidth)) / (2 * slopeWidth);
return -Math.cos(t * Math.PI);
} else if (value < target + featureWidth) {
double t = (value - (target + slopeWidth)) / (featureWidth - slopeWidth);
return +0.5 + Math.cos(t * Math.PI) / 2;
} else {
return 0;
}
};
}
}

View File

@ -0,0 +1,148 @@
/*
* 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.Set;
import java.util.function.Consumer;
import com.google.common.collect.ImmutableSet;
import glm.vec._3.i.Vec3i;
import kdotjpg.opensimplex2.areagen.OpenSimplex2S;
import ru.windcorp.progressia.common.util.VectorUtil;
import ru.windcorp.progressia.common.world.block.BlockData;
import ru.windcorp.progressia.common.world.rels.RelFace;
import ru.windcorp.progressia.server.world.generation.surface.SurfaceFloatField;
import ru.windcorp.progressia.server.world.generation.surface.SurfaceTopLayerFeature;
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceBlockContext;
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceWorldContext;
public abstract class MultiblockVegetationFeature extends SurfaceTopLayerFeature {
private final SurfaceFloatField selector;
private final OpenSimplex2S wavinessGenerator = new OpenSimplex2S(0);
private final double maximumDensity;
private final Set<String> soilWhitelist;
public MultiblockVegetationFeature(String id, SurfaceFloatField selector, double minimumPeriod) {
super(id);
this.selector = selector;
this.maximumDensity = 1 / minimumPeriod;
ImmutableSet.Builder<String> soilWhitelistBuilder = ImmutableSet.builder();
createSoilWhitelist(soilWhitelistBuilder::add);
this.soilWhitelist = soilWhitelistBuilder.build();
}
protected void createSoilWhitelist(Consumer<String> output) {
output.accept("Test:Dirt");
}
@Override
protected void processTopBlock(SurfaceBlockContext context) {
Vec3i location = context.getLocation();
if (location.z < 0) {
return;
}
if (!soilWhitelist.isEmpty() && !soilWhitelist.contains(context.getBlock().getId())) {
return;
}
double selectorValue = selector.get(context);
double chance = selectorValue * maximumDensity;
if (context.getRandom().nextDouble() >= chance) {
return;
}
grow(context, selectorValue);
}
protected abstract void grow(SurfaceBlockContext context, double selectorValue);
@Override
protected boolean isSolid(SurfaceBlockContext context) {
return context.logic().getBlock().isSolid(RelFace.UP);
}
/*
* Utilities
*/
protected void setLeaves(SurfaceWorldContext context, Vec3i location, BlockData leaves) {
if (context.getBlock(location).getId().equals("Test:Air")) {
context.setBlock(location, leaves);
}
}
protected void iterateBlob(
Vec3i center,
double horDiameter,
double vertDiameter,
double wavinessAmplitude,
double wavinessScale,
Consumer<Vec3i> action
) {
VectorUtil.iterateCuboidAround(
center.x,
center.y,
center.z,
(int) Math.ceil(horDiameter) / 2 * 2 + 5,
(int) Math.ceil(horDiameter) / 2 * 2 + 5,
(int) Math.ceil(vertDiameter) / 2 * 2 + 5,
pos -> {
double sx = (pos.x - center.x) / horDiameter;
double sy = (pos.y - center.y) / horDiameter;
double sz = (pos.z - center.z) / vertDiameter;
double radius = 1;
if (wavinessAmplitude > 0) {
radius += wavinessAmplitude * wavinessGenerator.noise3_Classic(
sx / wavinessScale,
sy / wavinessScale,
sz / wavinessScale
);
}
if (sx * sx + sy * sy + sz * sz <= radius * radius) {
action.accept(pos);
}
}
);
}
protected void iterateSpheroid(
Vec3i center,
double horDiameter,
double vertDiameter,
Consumer<Vec3i> action
) {
iterateBlob(center, horDiameter, vertDiameter, 0, 0, action);
}
protected void iterateSphere(Vec3i center, double diameter, Consumer<Vec3i> action) {
iterateBlob(center, diameter, diameter, 0, 0, action);
}
}

View File

@ -0,0 +1,49 @@
/*
* 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 glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.block.BlockData;
import ru.windcorp.progressia.common.world.block.BlockDataRegistry;
import ru.windcorp.progressia.server.world.generation.surface.SurfaceFloatField;
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceBlockContext;
public class TestBushFeature extends MultiblockVegetationFeature {
private final BlockData trunk = BlockDataRegistry.getInstance().get("Test:Log");
private final BlockData leaves = BlockDataRegistry.getInstance().get("Test:TemporaryLeaves");
public TestBushFeature(String id, SurfaceFloatField selector) {
super(id, selector, 7 * 7);
}
@Override
protected void grow(SurfaceBlockContext context, double selectorValue) {
double size = selectorValue * randomDouble(context, 0.8, 1.2);
Vec3i center = context.getLocation().add_(0, 0, 1);
context.setBlock(center, trunk);
context.setBlock(center.add_(0, 0, 1), leaves);
iterateBlob(center, stretch(size, 1.3, 2.5), stretch(size, 0.6, 1.5), 0.7, 2, p -> {
setLeaves(context, p, leaves);
});
}
}

View File

@ -0,0 +1,118 @@
/*
* 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 static ru.windcorp.progressia.test.gen.Fields.*;
import java.util.ArrayList;
import java.util.List;
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.world.Coordinates;
import ru.windcorp.progressia.common.world.block.BlockData;
import ru.windcorp.progressia.common.world.block.BlockDataRegistry;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.world.generation.WorldGenerator;
import ru.windcorp.progressia.server.world.generation.planet.Planet;
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;
public class TestGenerationConfig {
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());
public static Function<Server, WorldGenerator> createGenerator() {
Planet planet = new Planet(
((int) PLANET_RADIUS) / Coordinates.CHUNK_SIZE,
SURFACE_GRAVITY,
CURVATURE,
INNER_RADIUS
);
TestHeightMap heightMap = new TestHeightMap(planet, planet.getRadius() / 4, FIELDS);
FloatRangeMap<TerrainLayer> layers = new ArrayFloatRangeMap<>();
registerTerrainLayers(layers);
List<SurfaceFeature> features = new ArrayList<>();
registerFeatures(features);
return server -> new PlanetGenerator("Test:PlanetGenerator", server, planet, heightMap, layers, features);
}
private static void registerTerrainLayers(FloatRangeMap<TerrainLayer> layers) {
BlockData granite = BlockDataRegistry.getInstance().get("Test:GraniteMonolith");
BlockData graniteCracked = BlockDataRegistry.getInstance().get("Test:GraniteCracked");
BlockData graniteGravel = BlockDataRegistry.getInstance().get("Test:GraniteGravel");
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;
}
} else {
return dirt;
}
});
layers.put(4, Float.POSITIVE_INFINITY, (f, n, w, d, r) -> granite);
}
private static void registerFeatures(List<SurfaceFeature> features) {
SurfaceFloatField forestiness = FIELDS.register(
"Test:Forestiness",
() -> squash(scale(FIELDS.primitive(), 200), 5)
);
SurfaceFloatField floweriness = FIELDS.register(
"Test:Floweriness",
f -> multiply(
scale(octaves(FIELDS.primitive(), 2, 2), 40),
tweak(FIELDS.get("Test:Forestiness", f), 1, -1, 1.1)
)
);
features.add(new TestBushFeature("Test:BushFeature", forestiness));
features.add(new TestTreeFeature("Test:TreeFeature", forestiness));
features.add(new TestGrassFeature("Test:GrassFeature", FIELDS.get("Test:CliffSelector"), floweriness));
}
}

View File

@ -0,0 +1,122 @@
/*
* 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.List;
import java.util.Set;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
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.generation.surface.SurfaceFloatField;
import ru.windcorp.progressia.server.world.generation.surface.SurfaceTopLayerFeature;
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceBlockContext;
public class TestGrassFeature extends SurfaceTopLayerFeature {
private static final Set<String> WHITELIST = ImmutableSet.of(
"Test:Dirt",
"Test:Stone",
"Test:GraniteMonolith",
"Test:GraniteCracked",
"Test:GraniteGravel"
);
private final SurfaceFloatField grassiness;
private final SurfaceFloatField floweriness;
private final double scatterDensity = 1.0 / (3*3);
private final TileData grass = TileDataRegistry.getInstance().get("Test:Grass");
private final List<TileData> flowers = ImmutableList.of(
TileDataRegistry.getInstance().get("Test:YellowFlowers")
);
private final List<TileData> scatter = ImmutableList.of(
TileDataRegistry.getInstance().get("Test:Stones"),
TileDataRegistry.getInstance().get("Test:Sand")
);
public TestGrassFeature(String id, SurfaceFloatField grassiness, SurfaceFloatField floweriness) {
super(id);
this.grassiness = grassiness;
this.floweriness = floweriness;
}
@Override
protected void processTopBlock(SurfaceBlockContext context) {
if (context.getLocation().z < 0) {
return;
}
if (!WHITELIST.contains(context.getBlock().getId())) {
return;
}
if (!context.pushRelative(RelFace.UP).logic().getBlock().isTransparent()) {
context.pop();
return;
}
context.pop();
double grassiness = this.grassiness.get(context);
if (grassiness < 0.2) {
growGrass(context);
}
placeScatter(context);
if (grassiness < 0.2) {
growFlowers(context);
}
}
private void placeScatter(SurfaceBlockContext context) {
if (context.getRandom().nextDouble() < scatterDensity) {
TileData tile = pickRandom(context, scatter);
context.addTile(RelFace.UP, tile);
}
}
private void growGrass(SurfaceBlockContext context) {
for (RelFace face : RelFace.getFaces()) {
if (face == RelFace.DOWN) continue;
if (context.pushRelative(face).logic().getBlock().isTransparent()) {
context.pop();
context.addTile(face, grass);
} else {
context.pop();
}
}
}
private void growFlowers(SurfaceBlockContext context) {
if (context.getRandom().nextDouble() < floweriness.get(context)) {
TileData tile = pickRandom(context, flowers);
context.addTile(RelFace.UP, tile);
}
}
@Override
protected boolean isSolid(SurfaceBlockContext context) {
return context.logic().getBlock().isSolid(RelFace.UP);
}
}

View File

@ -0,0 +1,79 @@
/*
* 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 static ru.windcorp.progressia.test.gen.Fields.*;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.server.world.generation.planet.Planet;
import ru.windcorp.progressia.server.world.generation.surface.SurfaceFloatField;
public class TestHeightMap implements SurfaceFloatField {
private final SurfaceFloatField shape;
public TestHeightMap(
Planet planet,
float cutoffThickness,
Fields fields
) {
for (AbsFace face : AbsFace.getFaces()) {
Field landmassDistribution = scale(octaves(fields.primitive(), 2, 5), 400);
Field landmasses = tweak(squash(landmassDistribution, 4), 1, 20, -20);
Field plainsSelector = squash(withMin(landmassDistribution, 0), 10);
Field plains = tweak(octaves(fields.primitive(), 2, 3), 100, 3);
Field randomCliffSelector = scale(fields.primitive(), 200);
randomCliffSelector = add(
select(randomCliffSelector, +0.7, 0.3),
amplify(select(randomCliffSelector, -0.7, 0.3), -1)
);
Field randomCliffs = octaves(scale(fields.primitive(), 300), 3, 5);
Field shoreCliffSelector = withMin(scale(fields.primitive(), 200), 0);
Field shoreCliffs = add(
landmassDistribution,
tweak(octaves(fields.primitive(), 2, 3), 50, 0.2)
);
fields.register("Test:CliffSelector", face, multiply(
shoreCliffSelector,
bias(select(shoreCliffs, 0, 0.07), 0)
));
fields.register("Test:Height", face, cutoff(add(
landmasses,
multiply(plains, plainsSelector),
multiply(amplify(cliff(randomCliffs, 0, 0.5, 0.03), 10), randomCliffSelector),
multiply(tweak(cliff(shoreCliffs, 0, 0.5, 0.03), 1, 15, 15), shoreCliffSelector)
), planet, cutoffThickness));
}
this.shape = fields.get("Test:Height");
}
@Override
public float get(AbsFace face, float x, float y) {
return (float) shape.get(face, x, y);
}
}

View File

@ -1,152 +0,0 @@
/*
* 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 kdotjpg.opensimplex2.areagen.OpenSimplex2S;
import ru.windcorp.progressia.server.world.DefaultWorldLogic;
class TestTerrainGenerator {
@FunctionalInterface
private interface Func2D {
double compute(double x, double y);
}
private final OpenSimplex2S noise;
private final Func2D shape;
public TestTerrainGenerator(TestWorldGenerator testWorldGenerator, DefaultWorldLogic world) {
this.noise = new OpenSimplex2S("We're getting somewhere".hashCode());
Func2D plainsHeight = tweak(
octaves(
tweak(primitive(), 0.01, 0.5),
2,
3
),
1,
0.2,
0.2
);
Func2D mountainsHeight = tweak(
octaves(
ridge(tweak(primitive(), 0.01, 1)),
2,
1.5,
12
),
1,
3
);
Func2D mountainousity = tweak(
octaves(
tweak(primitive(), 0.007, 1),
2,
3
),
1,
1,
-0.25
);
shape = tweak(
add(multiply(squash(mountainousity, 10), mountainsHeight), plainsHeight),
0.001,
1000,
0
);
}
public void compute(int startX, int startY, double[][] heightMap, double[][] slopeMap) {
for (int x = 0; x < heightMap.length; ++x) {
for (int y = 0; y < heightMap.length; ++y) {
heightMap[x][y] = shape.compute(x + startX, y + startY);
slopeMap[x][y] = computeSlope(shape, x + startX, y + startY, heightMap[x][y]);
}
}
}
private double computeSlope(Func2D f, double x0, double y0, double f0) {
double di = 0.5;
double dfdx = (f.compute(x0 + di, y0) - f0) / di;
double dfdy = (f.compute(x0, y0 + di) - f0) / di;
return Math.hypot(dfdx, dfdy);
}
/*
* Utility functions
*/
private Func2D primitive() {
return noise::noise2;
}
private Func2D add(Func2D a, Func2D b) {
return (x, y) -> a.compute(x, y) + b.compute(x, y);
}
private Func2D multiply(Func2D a, Func2D b) {
return (x, y) -> a.compute(x, y) * b.compute(x, y);
}
private Func2D tweak(Func2D f, double scale, double amplitude, double bias) {
return (x, y) -> f.compute(x * scale, y * scale) * amplitude + bias;
}
private Func2D tweak(Func2D f, double scale, double amplitude) {
return tweak(f, scale, amplitude, 0);
}
private Func2D octaves(Func2D f, double scaleFactor, double amplitudeFactor, int octaves) {
return (x, y) -> {
double result = 0;
double scale = 1;
double amplitude = 1;
for (int i = 0; i < octaves; ++i) {
result += f.compute(x * scale, y * scale) * amplitude;
scale *= scaleFactor;
amplitude /= amplitudeFactor;
}
return result;
};
}
private Func2D octaves(Func2D f, double factor, int octaves) {
return octaves(f, factor, factor, octaves);
}
private Func2D squash(Func2D f, double slope) {
return (x, y) -> 1 / (1 + Math.exp(-slope * f.compute(x, y)));
}
private Func2D ridge(Func2D f) {
return (x, y) -> {
double result = 1 - Math.abs(f.compute(x, y));
return result * result;
};
}
}

View File

@ -0,0 +1,69 @@
/*
* 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 glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.block.BlockData;
import ru.windcorp.progressia.common.world.block.BlockDataRegistry;
import ru.windcorp.progressia.server.world.generation.surface.SurfaceFloatField;
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceBlockContext;
public class TestTreeFeature extends MultiblockVegetationFeature {
private final BlockData trunk = BlockDataRegistry.getInstance().get("Test:Log");
private final BlockData leaves = BlockDataRegistry.getInstance().get("Test:TemporaryLeaves");
public TestTreeFeature(String id, SurfaceFloatField selector) {
super(id, selector, 10 * 10);
}
@Override
protected void grow(SurfaceBlockContext context, double selectorValue) {
Vec3i start = context.getLocation().add_(0, 0, 1);
Vec3i center = start.add_(0);
double size = selectorValue * randomDouble(context, 0.8, 1.2);
int height = (int) stretch(size, 3, 7);
for (; center.z < start.z + height; ++center.z) {
context.setBlock(center, trunk);
}
double branchHorDistance = 0;
do {
double branchSize = 0.5 + randomDouble(context, 1, 2) * size;
double branchHorAngle = randomDouble(context, 0, 2 * Math.PI);
int branchVertOffset = (int) randomDouble(context, -2, 0);
Vec3i branchCenter = center.add_(
(int) (Math.sin(branchHorAngle) * branchHorDistance),
(int) (Math.cos(branchHorAngle) * branchHorDistance),
branchVertOffset
);
iterateBlob(branchCenter, 1 * branchSize, 2.3 * branchSize, 0.5, 3, p -> {
setLeaves(context, p, leaves);
});
branchHorDistance = randomDouble(context, 0.7, 1.5);
} while (context.getRandom().nextInt(8) > 1);
}
}

View File

@ -1,400 +0,0 @@
/*
* 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.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Random;
import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.util.VectorUtil;
import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.Coordinates;
import ru.windcorp.progressia.common.world.DecodingException;
import ru.windcorp.progressia.common.world.DefaultWorldData;
import ru.windcorp.progressia.common.world.WorldDataListener;
import ru.windcorp.progressia.common.world.block.BlockData;
import ru.windcorp.progressia.common.world.block.BlockDataRegistry;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.common.world.tile.TileData;
import ru.windcorp.progressia.common.world.tile.TileDataRegistry;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.world.generation.AbstractWorldGenerator;
public class TestWorldGenerator extends AbstractWorldGenerator<Boolean> {
private final TestTerrainGenerator terrainGen;
public TestWorldGenerator(Server server) {
super("Test:WorldGenerator", server, Boolean.class, "Test:TheGravityModel");
this.terrainGen = new TestTerrainGenerator(this, server.getWorld());
getWorldData().addListener(new WorldDataListener() {
@Override
public void onChunkLoaded(DefaultWorldData world, DefaultChunkData chunk) {
findAndPopulate(chunk.getPosition(), world);
}
});
}
@Override
public Vec3 suggestSpawnLocation() {
return new Vec3(8, 8, 880);
}
@Override
protected Boolean doReadGenerationHint(DataInputStream input) throws IOException, DecodingException {
return input.readBoolean();
}
@Override
protected void doWriteGenerationHint(DataOutputStream output, Boolean hint) throws IOException {
output.writeBoolean(hint);
}
@Override
protected boolean checkIsChunkReady(Boolean hint) {
return hint;
}
@Override
public DefaultChunkData generate(Vec3i chunkPos) {
DefaultChunkData chunk = generateUnpopulated(chunkPos, getWorldData());
getWorldData().addChunk(chunk);
return chunk;
}
private DefaultChunkData generateUnpopulated(Vec3i chunkPos, DefaultWorldData world) {
DefaultChunkData chunk = new DefaultChunkData(chunkPos, world);
chunk.setGenerationHint(false);
final int bpc = DefaultChunkData.BLOCKS_PER_CHUNK;
Random random = new Random(chunkPos.x + chunkPos.y + chunkPos.z);
BlockData dirt = BlockDataRegistry.getInstance().get("Test:Dirt");
BlockData stone = BlockDataRegistry.getInstance().get("Test:Stone");
BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
BlockData[] granites = new BlockData[] {
BlockDataRegistry.getInstance().get("Test:GraniteGravel"),
BlockDataRegistry.getInstance().get("Test:GraniteGravel"),
BlockDataRegistry.getInstance().get("Test:GraniteCracked"),
BlockDataRegistry.getInstance().get("Test:GraniteMonolith")
};
double[][] heightMap = new double[bpc][bpc];
double[][] gradMap = new double[bpc][bpc];
int startX = Coordinates.getInWorld(chunk.getX(), 0);
int startY = Coordinates.getInWorld(chunk.getY(), 0);
int startZ = Coordinates.getInWorld(chunk.getZ(), 0);
terrainGen.compute(startX, startY, heightMap, gradMap);
VectorUtil.iterateCuboid(0, 0, 0, bpc, bpc, bpc, pos -> {
double layer = pos.z - heightMap[pos.x][pos.y] + startZ;
if (layer < -4) {
chunk.setBlock(pos, stone, false);
} else if (layer < 0) {
if (gradMap[pos.x][pos.y] > 0.5) {
BlockData granite = granites[random.nextInt(4)];
chunk.setBlock(pos, granite, false);
} else {
chunk.setBlock(pos, dirt, false);
}
} else {
chunk.setBlock(pos, air, false);
}
});
return chunk;
}
private void findAndPopulate(Vec3i changePos, DefaultWorldData world) {
VectorUtil.iterateCuboidAround(changePos, 3, candidatePos -> {
if (canBePopulated(candidatePos, world)) {
populate(candidatePos, world);
}
});
}
private boolean canBePopulated(Vec3i candidatePos, DefaultWorldData world) {
Vec3i cursor = Vectors.grab3i();
DefaultChunkData candidate = world.getChunk(candidatePos);
if (candidate == null || isChunkReady(candidate.getGenerationHint()))
return false;
for (int dx = -1; dx <= 1; ++dx) {
cursor.x = candidatePos.x + dx;
for (int dy = -1; dy <= 1; ++dy) {
cursor.y = candidatePos.y + dy;
for (int dz = -1; dz <= 1; ++dz) {
if ((dx | dy | dz) == 0)
continue;
cursor.z = candidatePos.z + dz;
DefaultChunkData chunk = world.getChunk(cursor);
if (chunk == null) {
return false;
}
}
}
}
Vectors.release(cursor);
return true;
}
private void populate(Vec3i chunkPos, DefaultWorldData world) {
Random random = new Random(chunkPos.x + chunkPos.y + chunkPos.z);
DefaultChunkData chunk = world.getChunk(chunkPos);
assert chunk != null : "Something went wrong when populating chunk at (" + chunkPos.x + "; " + chunkPos.y + "; "
+ chunkPos.z + ")";
BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
BlockData dirt = BlockDataRegistry.getInstance().get("Test:Dirt");
Vec3i biw = new Vec3i();
int minX = chunk.getMinX();
int maxX = chunk.getMaxX() + 1;
int minY = chunk.getMinY();
int maxY = chunk.getMaxY() + 1;
int minZ = chunk.getMinZ();
int maxZ = chunk.getMaxZ() + 1;
final int bpc = DefaultChunkData.BLOCKS_PER_CHUNK;
double[][] heightMap = new double[bpc][bpc];
double[][] gradMap = new double[bpc][bpc];
terrainGen.compute(minX, minY, heightMap, gradMap);
for (biw.x = minX; biw.x < maxX; ++biw.x) {
for (biw.y = minY; biw.y < maxY; ++biw.y) {
for (biw.z = minZ; biw.z < maxZ + 1 && world.getBlock(biw) != air; ++biw.z)
;
biw.z -= 1;
if (biw.z == maxZ)
continue;
if (biw.z < minZ)
continue;
int xic = Coordinates.convertInWorldToInChunk(biw.x);
int yic = Coordinates.convertInWorldToInChunk(biw.y);
addTiles(
chunk,
biw,
world,
random,
world.getBlock(biw) == dirt,
heightMap[xic][yic],
gradMap[xic][yic]
);
}
}
chunk.setGenerationHint(true);
}
private void addTiles(
DefaultChunkData chunk,
Vec3i biw,
DefaultWorldData world,
Random random,
boolean isDirt,
double height,
double grad
) {
if (isDirt)
addGrass(chunk, biw, world, random);
addDecor(chunk, biw, world, random, isDirt);
addSnow(chunk, biw, world, random, isDirt, height, grad);
}
private void addGrass(DefaultChunkData chunk, Vec3i biw, DefaultWorldData world, Random random) {
BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
TileData grass = TileDataRegistry.getInstance().get("Test:Grass");
world.getTiles(biw, AbsFace.POS_Z).add(grass);
for (AbsFace face : AbsFace.getFaces()) {
if (face.getVector().z != 0)
continue;
biw.add(face.getVector());
if (world.getBlock(biw) == air) {
biw.sub(face.getVector());
world.getTiles(biw, face).add(grass);
} else {
biw.sub(face.getVector());
}
}
}
private void addDecor(DefaultChunkData chunk, Vec3i biw, DefaultWorldData world, Random random, boolean isDirt) {
if (isDirt) {
if (random.nextInt(8) == 0) {
world.getTiles(biw, AbsFace.POS_Z).addFarthest(
TileDataRegistry.getInstance().get("Test:Sand")
);
}
if (random.nextInt(8) == 0) {
world.getTiles(biw, AbsFace.POS_Z).addFarthest(
TileDataRegistry.getInstance().get("Test:Stones")
);
}
if (random.nextInt(8) == 0) {
world.getTiles(biw, AbsFace.POS_Z).addFarthest(
TileDataRegistry.getInstance().get("Test:YellowFlowers")
);
}
} else {
if (random.nextInt(2) == 0) {
world.getTiles(biw, AbsFace.POS_Z).addFarthest(
TileDataRegistry.getInstance().get("Test:Stones")
);
}
}
}
private void addSnow(
DefaultChunkData chunk,
Vec3i biw,
DefaultWorldData world,
Random random,
boolean isDirt,
double height,
double grad
) {
if (height < 1500)
return;
BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
double quarterChance = computeSnowQuarterChance(height, grad);
double halfChance = computeSnowHalfChance(height, grad);
double opaqueChance = computeSnowOpaqueChance(height, grad);
for (AbsFace face : AbsFace.getFaces()) {
if (face == AbsFace.NEG_Z)
continue;
if (face.getVector().z == 0) {
biw.add(face.getVector());
BlockData neighbour = world.getBlock(biw);
biw.sub(face.getVector());
if (neighbour != air)
continue;
}
TileData tile;
double maxValue = height > 3000 ? 3 : (1 + 2 * ((height - 1500) / 1500));
double value = random.nextDouble() * maxValue;
if (value < quarterChance) {
tile = TileDataRegistry.getInstance().get("Test:SnowQuarter");
} else if ((value -= quarterChance) < halfChance) {
tile = TileDataRegistry.getInstance().get("Test:SnowHalf");
} else if ((value -= halfChance) < opaqueChance) {
tile = TileDataRegistry.getInstance().get("Test:SnowOpaque");
} else {
tile = null;
}
if (tile != null) {
world.getTiles(biw, face).addFarthest(tile);
}
}
}
private double computeSnowQuarterChance(double height, double grad) {
double heightCoeff;
if (height < 1500)
heightCoeff = 0;
else if (height < 2000)
heightCoeff = (height - 1500) / 500;
else
heightCoeff = 1;
if (heightCoeff < 1e-4)
return 0;
double gradCoeff = computeSnowGradCoeff(height, grad);
return heightCoeff * gradCoeff;
}
private double computeSnowHalfChance(double height, double grad) {
double heightCoeff;
if (height < 2000)
heightCoeff = 0;
else if (height < 2500)
heightCoeff = (height - 2000) / 500;
else
heightCoeff = 1;
if (heightCoeff < 1e-4)
return 0;
double gradCoeff = computeSnowGradCoeff(height, grad);
return heightCoeff * gradCoeff;
}
private double computeSnowOpaqueChance(double height, double grad) {
double heightCoeff;
if (height < 2500)
heightCoeff = 0;
else if (height < 3000)
heightCoeff = (height - 2500) / 500;
else
heightCoeff = 1;
if (heightCoeff < 1e-4)
return 0;
double gradCoeff = computeSnowGradCoeff(height, grad);
return heightCoeff * gradCoeff;
}
private double computeSnowGradCoeff(double height, double grad) {
final double a = -0.00466666666666667;
final double b = 12.66666666666667;
double characteristicGrad = 1 / (a * height + b);
return Math.exp(-grad / characteristicGrad);
}
}

View File

@ -1,70 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.test.gen.planet;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.test.gen.surface.SurfaceFloatField;
public class TestHeightMap implements SurfaceFloatField {
private final float cutoffPoint;
private final float cutoffDistance;
private final float amplitude;
private final float characteristicSize;
public TestHeightMap(
float cutoffPoint,
float cutoffDistance,
float amplitude,
float characteristicSize
) {
this.cutoffPoint = cutoffPoint;
this.cutoffDistance = cutoffDistance;
this.amplitude = amplitude;
this.characteristicSize = characteristicSize;
}
@Override
public float get(AbsFace face, float north, float west) {
double cutoffCoefficient = 1;
cutoffCoefficient *= cutoffFunction(cutoffPoint - north);
cutoffCoefficient *= cutoffFunction(cutoffPoint + north);
cutoffCoefficient *= cutoffFunction(cutoffPoint - west);
cutoffCoefficient *= cutoffFunction(cutoffPoint + west);
if (cutoffCoefficient == 0) {
return 0;
}
double base = Math.sin(north / characteristicSize) * Math.sin(west / characteristicSize);
base *= amplitude;
return (float) (base * cutoffCoefficient);
}
private double cutoffFunction(float distanceToCutoffPoint) {
if (distanceToCutoffPoint < 0) {
return 0;
} else if (distanceToCutoffPoint < cutoffDistance) {
return (1 - Math.cos(Math.PI * distanceToCutoffPoint / cutoffDistance)) / 2;
} else {
return 1;
}
}
}

View File

@ -1,77 +0,0 @@
/*
* 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.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.rels.AbsFace;
/**
* A scalar field defined on a plane. For each pair of {@code float}s (north; west) a single {@code float} is defined by this object.
*/
public abstract class SurfaceCachingFloatField extends Namespaced implements SurfaceFloatField {
private final int levels;
private SurfaceFieldRegistry registry = null;
private int index;
public SurfaceCachingFloatField(String id, int levels) {
super(id);
this.levels = levels;
}
int getIndex() {
if (getRegistry() == null) {
throw new IllegalStateException("No registry assigned to field " + this);
}
return index;
}
void setIndex(int index) {
if (getRegistry() == null) {
throw new IllegalStateException("No registry assigned to field " + this);
}
this.index = index;
}
SurfaceFieldRegistry getRegistry() {
return registry;
}
void setRegistry(SurfaceFieldRegistry registry) {
this.registry = registry;
}
public int getLevels() {
return levels;
}
protected abstract float computeDetailAt(AbsFace surface, int level, float north, float west);
@Override
public float get(AbsFace surface, float north, float west) {
float result = 0;
for (int level = 0; level < getLevels(); ++level) {
result += computeDetailAt(surface, level, north, west);
}
return result;
}
}

View File

@ -1,29 +0,0 @@
/*
* 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;
public class SurfaceFieldRegistry {
private int nextIndex = 0;
public void register(SurfaceCachingFloatField field) {
field.setIndex(nextIndex);
nextIndex++;
}
}

View File

@ -1,57 +0,0 @@
/*
* 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.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import gnu.trove.map.TLongObjectMap;
import gnu.trove.map.hash.TLongObjectHashMap;
import ru.windcorp.progressia.common.util.CoordinatePacker;
import ru.windcorp.progressia.common.world.DecodingException;
public class SurfaceNodeStorage {
public static class Node {
// private float[] floats;
}
private final TLongObjectMap<Node> map = new TLongObjectHashMap<>();
public Node getNode(int north, int west) {
return map.get(CoordinatePacker.pack2IntsIntoLong(north, west));
}
public boolean hasNode(int north, int west) {
return map.containsKey(CoordinatePacker.pack2IntsIntoLong(north, west));
}
public void put(int north, int west, Node node) {
map.put(CoordinatePacker.pack2IntsIntoLong(north, west), node);
}
public void read(DataInput input) throws IOException, DecodingException {
System.err.println("PlaneNodeMap.read did nothing because nobody implemented it yet");
}
public void write(DataOutput output) throws IOException {
System.err.println("PlaneNodeMap.write did nothing because nobody implemented it yet");
}
}