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:
parent
15b5d367b4
commit
2328f2ae3a
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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()));
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
||||
}
|
@ -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;
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
321
src/main/java/ru/windcorp/progressia/test/gen/Fields.java
Normal file
321
src/main/java/ru/windcorp/progressia/test/gen/Fields.java
Normal 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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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++;
|
||||
}
|
||||
|
||||
}
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user