From 4ac5660fded3553230da6173efa40e39aa603879 Mon Sep 17 00:00:00 2001 From: Treer Date: Wed, 25 Apr 2018 19:09:01 +1000 Subject: [PATCH] Add Carpathian mapgen --- .../world/DefaultVersionFeatures.java | 10 +- .../minetest/world/mapgen/Constants.java | 6 + .../world/mapgen/MapgenCarpathianParams.java | 56 +++++ .../oracle/BiomeDataOracleCarpathian.java | 206 ++++++++++++++++++ .../world/oracle/BiomeDataOracleV6.java | 1 - .../world/oracle/BiomeDataOracleV7.java | 10 +- 6 files changed, 276 insertions(+), 13 deletions(-) create mode 100644 src/main/java/amidst/minetest/world/mapgen/MapgenCarpathianParams.java create mode 100644 src/main/java/amidst/minetest/world/oracle/BiomeDataOracleCarpathian.java diff --git a/src/main/java/amidst/minetest/world/DefaultVersionFeatures.java b/src/main/java/amidst/minetest/world/DefaultVersionFeatures.java index c44901eb..985e316e 100644 --- a/src/main/java/amidst/minetest/world/DefaultVersionFeatures.java +++ b/src/main/java/amidst/minetest/world/DefaultVersionFeatures.java @@ -54,10 +54,10 @@ public class DefaultVersionFeatures implements VersionFeaturesFactory { this.worldTypes = new WorldTypes( new WorldType[]{ - //WorldType.CARPATHIAN, WorldType.V7, WorldType.V6, WorldType.V5, + WorldType.CARPATHIAN, WorldType.FLAT, //WorldType.FRACTAL } @@ -75,9 +75,7 @@ public class DefaultVersionFeatures implements VersionFeaturesFactory { VersionFeature. listBuilder() .init(commonLayers) .initExtend( - LayerIds.MINETEST_OCEAN, - LayerIds.MINETEST_RIVER, - LayerIds.MINETEST_MOUNTAIN + LayerIds.MINETEST_OCEAN ).construct() ); enabledLayers.put(WorldType.V7, @@ -131,6 +129,10 @@ public class DefaultVersionFeatures implements VersionFeaturesFactory { WorldType.V5, (seed, mapgenParams, biomeProfile) -> new amidst.minetest.world.oracle.BiomeDataOracleV5(mapgenParams, biomeProfile, seed) ), + new AbstractMap.SimpleEntry>( + WorldType.CARPATHIAN, + (seed, mapgenParams, biomeProfile) -> new amidst.minetest.world.oracle.BiomeDataOracleCarpathian(mapgenParams, biomeProfile, seed) + ), new AbstractMap.SimpleEntry>( WorldType.FLAT, (seed, mapgenParams, biomeProfile) -> new amidst.minetest.world.oracle.BiomeDataOracleFlat(mapgenParams, biomeProfile, seed) diff --git a/src/main/java/amidst/minetest/world/mapgen/Constants.java b/src/main/java/amidst/minetest/world/mapgen/Constants.java index 9782c646..b8323af7 100644 --- a/src/main/java/amidst/minetest/world/mapgen/Constants.java +++ b/src/main/java/amidst/minetest/world/mapgen/Constants.java @@ -7,4 +7,10 @@ public class Constants { // a Minetest Chunk is 5 x 5 x 5 MapBlocks! public static final int MAP_BLOCKSIZE = 16; + // The absolute working limit is (2^15 - viewing_range). + // I really don't want to make every algorithm to check if it's going near + // the limit or not, so this is lower. + // This is the maximum value the setting map_generation_limit can be + public static final short MAX_MAP_GENERATION_LIMIT = 31000; + } diff --git a/src/main/java/amidst/minetest/world/mapgen/MapgenCarpathianParams.java b/src/main/java/amidst/minetest/world/mapgen/MapgenCarpathianParams.java new file mode 100644 index 00000000..8108d341 --- /dev/null +++ b/src/main/java/amidst/minetest/world/mapgen/MapgenCarpathianParams.java @@ -0,0 +1,56 @@ +package amidst.minetest.world.mapgen; + +import javax.vecmath.Vector3f; + +import amidst.mojangapi.world.WorldType; +import amidst.mojangapi.world.biome.BiomeColor; + +// TODO: be able to import these values from map_meta.txt files +public class MapgenCarpathianParams extends MapgenParams { + + public static final int FLAG_CARPATHIAN_CAVERNS = 0x01; + + public int spflags = FLAG_CARPATHIAN_CAVERNS; + + public NoiseParams np_base = new NoiseParams(12, 1, new Vector3f(2557, 2557, 2557), 6538, (short)4, 0.8f, 0.5f); + public NoiseParams np_height1 = new NoiseParams(0, 5, new Vector3f(251, 251, 251), 9613, (short)5, 0.5f, 2.0f); + public NoiseParams np_height2 = new NoiseParams(0, 5, new Vector3f(383, 383, 383), 1949, (short)5, 0.5f, 2.0f); + public NoiseParams np_height3 = new NoiseParams(0, 5, new Vector3f(509, 509, 509), 3211, (short)5, 0.5f, 2.0f); + public NoiseParams np_height4 = new NoiseParams(0, 5, new Vector3f(631, 631, 631), 1583, (short)5, 0.5f, 2.0f); + public NoiseParams np_hills_terrain = new NoiseParams(1, 1, new Vector3f(1301, 1301, 1301), 1692, (short)5, 0.5f, 2.0f); + public NoiseParams np_ridge_terrain = new NoiseParams(1, 1, new Vector3f(1889, 1889, 1889), 3568, (short)5, 0.5f, 2.0f); + public NoiseParams np_step_terrain = new NoiseParams(1, 1, new Vector3f(1889, 1889, 1889), 4157, (short)5, 0.5f, 2.0f); + public NoiseParams np_hills = new NoiseParams(0, 3, new Vector3f(257, 257, 257), 6604, (short)6, 0.5f, 2.0f); + public NoiseParams np_ridge_mnt = new NoiseParams(0, 12, new Vector3f(743, 743, 743), 5520, (short)6, 0.7f, 2.0f); + public NoiseParams np_step_mnt = new NoiseParams(0, 8, new Vector3f(509, 509, 509), 2590, (short)6, 0.6f, 2.0f); + public NoiseParams np_mnt_var = new NoiseParams(0, 1, new Vector3f(499, 499, 499), 2490, (short)5, 0.55f, 2.0f); + + + @Override + public String toString() { + String prefix = "mgcarpathian_"; + StringBuilder result = new StringBuilder(); + result.append("mgcarpathian_spflags = "); + appendFlags(result, spflags, new String[] {"caverns"}); + result.append("\r\n"); + result.append(super.toString()); + result.append(np_base.toString( prefix +"np_base")); + result.append(np_height1.toString( prefix +"np_height1")); + result.append(np_height2.toString( prefix +"np_height2")); + result.append(np_height3.toString( prefix +"np_height3")); + result.append(np_height4.toString( prefix +"np_height4")); + result.append(np_hills_terrain.toString(prefix +"np_hills_terrain")); + result.append(np_ridge_terrain.toString(prefix +"np_ridge_terrain")); + result.append(np_step_terrain.toString( prefix +"np_step_terrain")); + result.append(np_hills.toString( prefix +"np_hills")); + result.append(np_ridge_mnt.toString( prefix +"np_ridge_mnt")); + result.append(np_step_mnt.toString( prefix +"np_step_mnt")); + result.append(np_mnt_var.toString( prefix +"np_mnt_var")); + return result.toString(); + } + + @Override + public WorldType getWorldType() { + return WorldType.CARPATHIAN; + } +} diff --git a/src/main/java/amidst/minetest/world/oracle/BiomeDataOracleCarpathian.java b/src/main/java/amidst/minetest/world/oracle/BiomeDataOracleCarpathian.java new file mode 100644 index 00000000..3136e325 --- /dev/null +++ b/src/main/java/amidst/minetest/world/oracle/BiomeDataOracleCarpathian.java @@ -0,0 +1,206 @@ +package amidst.minetest.world.oracle; + +import amidst.documentation.Immutable; +import amidst.logging.AmidstLogger; +import amidst.logging.AmidstMessageBox; +import amidst.minetest.world.mapgen.Constants; +import amidst.minetest.world.mapgen.InvalidNoiseParamsException; +import amidst.minetest.world.mapgen.MapgenCarpathianParams; +import amidst.minetest.world.mapgen.MapgenParams; +import amidst.minetest.world.mapgen.MinetestBiome; +import amidst.minetest.world.mapgen.Noise; +import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Resolution; +import amidst.settings.biomeprofile.BiomeProfileSelection; + +@Immutable +public class BiomeDataOracleCarpathian extends MinetestBiomeDataOracle { + private final MapgenCarpathianParams carpathianParams; + + private Noise noise_base; + private Noise noise_height1; + private Noise noise_height2; + private Noise noise_height3; + private Noise noise_height4; + private Noise noise_hills_terrain; + private Noise noise_ridge_terrain; + private Noise noise_step_terrain; + private Noise noise_hills; + private Noise noise_ridge_mnt; + private Noise noise_step_mnt; + private Noise noise_mnt_var; + + private int grad_wl; + + /** + * @param mapgenCarpathianParams + * @param biomeProfileSelection - if null then a default biomeprofile will be used + * @param seed + * @throws InvalidNoiseParamsException + */ + public BiomeDataOracleCarpathian(MapgenParams mapgenCarpathianParams, BiomeProfileSelection biomeProfileSelection, long seed) { + + super(mapgenCarpathianParams, biomeProfileSelection, seed); + + if (params instanceof MapgenCarpathianParams) { + carpathianParams = (MapgenCarpathianParams)params; + } else { + AmidstLogger.error("Error: BiomeDataOracleCarpathian cannot cast params to CarpathianParams. Using defaults instead."); + this.params = carpathianParams = new MapgenCarpathianParams(); + } + + grad_wl = 1 - params.water_level; + + try { + // 2D noise + noise_base = new Noise(carpathianParams.np_base, this.seed, params.chunk_length_x, params.chunk_length_z); + noise_height1 = new Noise(carpathianParams.np_height1, this.seed, params.chunk_length_x, params.chunk_length_z); + noise_height2 = new Noise(carpathianParams.np_height2, this.seed, params.chunk_length_x, params.chunk_length_z); + noise_height3 = new Noise(carpathianParams.np_height3, this.seed, params.chunk_length_x, params.chunk_length_z); + noise_height4 = new Noise(carpathianParams.np_height4, this.seed, params.chunk_length_x, params.chunk_length_z); + noise_hills_terrain = new Noise(carpathianParams.np_hills_terrain, this.seed, params.chunk_length_x, params.chunk_length_z); + noise_ridge_terrain = new Noise(carpathianParams.np_ridge_terrain, this.seed, params.chunk_length_x, params.chunk_length_z); + noise_step_terrain = new Noise(carpathianParams.np_step_terrain, this.seed, params.chunk_length_x, params.chunk_length_z); + noise_hills = new Noise(carpathianParams.np_hills, this.seed, params.chunk_length_x, params.chunk_length_z); + noise_ridge_mnt = new Noise(carpathianParams.np_ridge_mnt, this.seed, params.chunk_length_x, params.chunk_length_z); + noise_step_mnt = new Noise(carpathianParams.np_step_mnt, this.seed, params.chunk_length_x, params.chunk_length_z); + + //// 3D terrain noise + // 1 up 1 down overgeneration + noise_mnt_var = new Noise(carpathianParams.np_mnt_var, this.seed, params.chunk_length_x, params.chunk_length_y + 2, params.chunk_length_z); + + } catch (InvalidNoiseParamsException ex) { + AmidstLogger.error("Invalid carpathianParams from Minetest game. " + ex); + ex.printStackTrace(); + } + } + + // Steps function + float getSteps(float noise) + { + float w = 0.5f; + float k = (float) Math.floor(noise / w); + float f = (noise - k * w) / w; + float s = Math.min(2.f * f, 1.f); + return (k + s) * w; + } + + float terrainLevelAtPoint(int x, int z) + { + float ground = Noise.NoisePerlin2D(noise_base.np, x, z, seed); + float height1 = Noise.NoisePerlin2D(noise_height1.np, x, z, seed); + float height2 = Noise.NoisePerlin2D(noise_height2.np, x, z, seed); + float height3 = Noise.NoisePerlin2D(noise_height3.np, x, z, seed); + float height4 = Noise.NoisePerlin2D(noise_height4.np, x, z, seed); + float hter = Noise.NoisePerlin2D(noise_hills_terrain.np, x, z, seed); + float rter = Noise.NoisePerlin2D(noise_ridge_terrain.np, x, z, seed); + float ster = Noise.NoisePerlin2D(noise_step_terrain.np, x, z, seed); + float n_hills = Noise.NoisePerlin2D(noise_hills.np, x, z, seed); + float n_ridge_mnt = Noise.NoisePerlin2D(noise_ridge_mnt.np, x, z, seed); + float n_step_mnt = Noise.NoisePerlin2D(noise_step_mnt.np, x, z, seed); + + int height = -Constants.MAX_MAP_GENERATION_LIMIT; + + int searchInc = 1; + + for (short y = 1; y <= 200; y += searchInc) { // we're going to break out of this loop when y is close to surface_level + float mnt_var = Noise.NoisePerlin3D(noise_mnt_var.np, x, y, z, seed); + + // Gradient & shallow seabed + int grad = (y < params.water_level) ? grad_wl + (params.water_level - y) * 3 : 1 - y; + + // Hill/Mountain height (hilliness) + // Java doesn't have inline functions, so expanding getLerp() + // getLerp(float noise1, float noise2, float mod) = noise1 + mod * (noise2 - noise1); + // was: + // float hill1 = getLerp(height1, height2, mnt_var); + // float hill2 = getLerp(height3, height4, mnt_var); + // float hill3 = getLerp(height3, height2, mnt_var); + // float hill4 = getLerp(height1, height4, mnt_var); + float hill1 = height1 + mnt_var * (height2 - height1); + float hill2 = height3 + mnt_var * (height4 - height3); + float hill3 = height3 + mnt_var * (height2 - height3); + float hill4 = height1 + mnt_var * (height4 - height1); + float hilliness = Math.max(Math.min(hill1, hill2), Math.min(hill3, hill4)); + + // Rolling hills + float hill_mnt = hilliness * (float)Math.pow(n_hills, 2.f); + float hills = (float)Math.pow(hter, 3.f) * hill_mnt; + + // Ridged mountains + float ridge_mnt = hilliness * (1.f - Math.abs(n_ridge_mnt)); + float ridged_mountains = (float)Math.pow(rter, 3.f) * ridge_mnt; + + // Step (terraced) mountains + float step_mnt = hilliness * getSteps(n_step_mnt); + float step_mountains = (float)Math.pow(ster, 3.f) * step_mnt; + + // Final terrain level + float mountains = hills + ridged_mountains + step_mountains; + float surface_level = ground + mountains + grad; + + height = (int)surface_level; + if ((y + (searchInc / 2)) >= surface_level) { + // Close enough. + // (should be properly accurate at water level, but will lose accuracy with height) + break; + } + if (y > 2 && searchInc < 12) searchInc += 2; + } + + return height; + } + + @Override + public short populateArray(CoordinatesInWorld corner, short[][] result, boolean useQuarterResolution) { + + // The Carpathian mapgen terrain is not yet stable. + // See https://forum.minetest.net/viewtopic.php?f=18&t=19132 + + int width = result.length; + if (width > 0) { + Resolution resolution = Resolution.from(useQuarterResolution); + int height = result[0].length; + int left = (int) corner.getX(); + int top = (int) corner.getY(); + int shift = resolution.getShift(); + int step = resolution.getStep(); + int world_z; + int world_x; + short biomeValue; + MinetestBiome[] biomes = getBiomeArray(); + + try { + for (int y = 0; y < height; y++) { + + world_z = top + (y << shift); + world_x = left; + + // Use -world_z because Minetest uses left-handed coordinates, while Minecraft + // and Amidst use right-handed coordinates. + world_z = -world_z; + + for (int x = 0; x < width; x++) { + + biomeValue = 0; + + // Add the ocean bitplane + int surface_y = (int)terrainLevelAtPoint(world_x, world_z); + if (surface_y < carpathianParams.water_level) biomeValue |= BITPLANE_OCEAN; + + // add the biome index + // (mask the bitplanes in case the biome returned is -1 (NONE) + biomeValue |= calcBiomeAtPoint(biomes, world_x, surface_y, world_z).getIndex() & MASK_BITPLANES; + + result[x][y] = biomeValue; + world_x += step; + } + } + } catch (Exception e) { + AmidstLogger.error(e); + AmidstMessageBox.displayError("Error", e); + } + } + return MASK_BITPLANES; + } +} diff --git a/src/main/java/amidst/minetest/world/oracle/BiomeDataOracleV6.java b/src/main/java/amidst/minetest/world/oracle/BiomeDataOracleV6.java index 6020a255..76ee16cb 100644 --- a/src/main/java/amidst/minetest/world/oracle/BiomeDataOracleV6.java +++ b/src/main/java/amidst/minetest/world/oracle/BiomeDataOracleV6.java @@ -4,7 +4,6 @@ import java.io.Console; import amidst.logging.AmidstLogger; import amidst.logging.AmidstMessageBox; -import amidst.minetest.world.mapgen.DefaultBiomes; import amidst.minetest.world.mapgen.InvalidNoiseParamsException; import amidst.minetest.world.mapgen.MapgenParams; import amidst.minetest.world.mapgen.MapgenV6Params; diff --git a/src/main/java/amidst/minetest/world/oracle/BiomeDataOracleV7.java b/src/main/java/amidst/minetest/world/oracle/BiomeDataOracleV7.java index cd962de5..86b1a019 100644 --- a/src/main/java/amidst/minetest/world/oracle/BiomeDataOracleV7.java +++ b/src/main/java/amidst/minetest/world/oracle/BiomeDataOracleV7.java @@ -10,7 +10,6 @@ import amidst.minetest.world.mapgen.MinetestBiome; import amidst.minetest.world.mapgen.Noise; import amidst.mojangapi.world.coordinates.CoordinatesInWorld; import amidst.mojangapi.world.coordinates.Resolution; -import amidst.settings.biomeprofile.BiomeProfile; import amidst.settings.biomeprofile.BiomeProfileSelection; @Immutable @@ -30,11 +29,6 @@ public class BiomeDataOracleV7 extends MinetestBiomeDataOracle { //private Noise noise_filler_depth; // commented out because it shouldn't been needed for the surface - /** - * Updated by onBiomeProfileUpdate event, can be null. - */ - private volatile BiomeProfile biomeProfile; - short mount_zero_level = 0; float float_mount_density = 0.6f; float float_mount_height = 128.0f; @@ -191,10 +185,10 @@ public class BiomeDataOracleV7 extends MinetestBiomeDataOracle { // Add the ocean bitplane int surface_y = (int)baseTerrainLevelAtPoint(world_x, world_z); - if (surface_y < v7params.water_level) biomeValue |= BITPLANE_OCEAN; + if (surface_y < params.water_level) biomeValue |= BITPLANE_OCEAN; // Add the mountains bitplane - int surfaceOrSeaLevel = Math.max(surface_y, v7params.water_level); + int surfaceOrSeaLevel = Math.max(surface_y, params.water_level); float mnt_h_n = Math.max(Noise.NoisePerlin2D(noise_mount_height.np, world_x, world_z, seed), 1.0f); float density_gradient = -((float)(surfaceOrSeaLevel - mount_zero_level) / mnt_h_n); float mnt_n1 = Noise.NoisePerlin3D(noise_mountain.np, world_x, surfaceOrSeaLevel, world_z, seed);