Add new Mapgen V7 floatland implementation (#9296)

Floatland structure is vertically-compressed 3D noise.
Uses a lacunarity of 1.618 (the golden ratio) for high quality
noise.
Floatlands appear between user-settable Y limits, with smooth
tapering at each limit.
Simple user-settable density adjustment.
Shadow propagation is disabled in and just below floatlands, no
shadows are cast on the world surface.
Can be reconfigured to create a solid upper world layer between
the Y limits, lakes/seas can be optionally added to this.
This commit is contained in:
Paramat 2020-05-14 22:27:54 +01:00 committed by GitHub
parent d76785b4c7
commit af0f7ac4a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 160 additions and 12 deletions

@ -1607,12 +1607,53 @@ mgv6_np_apple_trees (Apple trees noise) noise_params_2d 0, 1, (100, 100, 100), 3
[*Mapgen V7] [*Mapgen V7]
# Map generation attributes specific to Mapgen v7. # Map generation attributes specific to Mapgen v7.
# 'ridges' enables the rivers. # 'ridges': Rivers.
# 'floatlands': Floating land masses in the atmosphere.
# 'caverns': Giant caves deep underground.
mgv7_spflags (Mapgen V7 specific flags) flags mountains,ridges,nofloatlands,caverns mountains,ridges,floatlands,caverns,nomountains,noridges,nofloatlands,nocaverns mgv7_spflags (Mapgen V7 specific flags) flags mountains,ridges,nofloatlands,caverns mountains,ridges,floatlands,caverns,nomountains,noridges,nofloatlands,nocaverns
# Y of mountain density gradient zero level. Used to shift mountains vertically. # Y of mountain density gradient zero level. Used to shift mountains vertically.
mgv7_mount_zero_level (Mountain zero level) int 0 mgv7_mount_zero_level (Mountain zero level) int 0
# Lower Y limit of floatlands.
mgv7_floatland_ymin (Floatland minimum Y) int 1024
# Upper Y limit of floatlands.
mgv7_floatland_ymax (Floatland maximum Y) int 4096
# Y-distance over which floatlands taper from full density to nothing.
# Tapering starts at this distance from the Y limit.
# For a solid floatland layer, this controls the height of hills/mountains.
# Must be less than or equal to half the distance between the Y limits.
mgv7_floatland_taper (Floatland tapering distance) int 256
# Exponent of the floatland tapering. Alters the tapering behaviour.
# Value = 1.0 creates a uniform, linear tapering.
# Values > 1.0 create a smooth tapering suitable for the default separated
# floatlands.
# Values < 1.0 (for example 0.25) create a more defined surface level with
# flatter lowlands, suitable for a solid floatland layer.
mgv7_float_taper_exp (Floatland taper exponent) float 2.0
# Adjusts the density of the floatland layer.
# Increase value to increase density. Can be positive or negative.
# Value = 0.0: 50% of volume is floatland.
# Value = 2.0 (can be higher depending on 'mgv7_np_floatland', always test
# to be sure) creates a solid floatland layer.
mgv7_floatland_density (Floatland density) float -0.9
# Surface level of optional water placed on a solid floatland layer.
# Water is disabled by default and will only be placed if this value is set
# to above 'mgv7_floatland_ymax' - 'mgv7_floatland_taper' (the start of the
# upper tapering).
# ***WARNING, POTENTIAL DANGER TO WORLDS AND SERVER PERFORMANCE***:
# When enabling water placement the floatlands must be configured and tested
# to be a solid layer by setting 'mgv7_floatland_density' to 2.0 (or other
# required value depending on 'mgv7_np_floatland'), to avoid
# server-intensive extreme water flow and to avoid vast flooding of the
# world surface below.
mgv7_floatland_ywater (Floatland water level) int -31000
# Controls width of tunnels, a smaller value creates wider tunnels. # Controls width of tunnels, a smaller value creates wider tunnels.
# Value >= 10.0 completely disables generation of tunnels and avoids the # Value >= 10.0 completely disables generation of tunnels and avoids the
# intensive noise calculations. # intensive noise calculations.
@ -1682,6 +1723,12 @@ mgv7_np_mountain (Mountain noise) noise_params_3d -0.6, 1, (250, 350, 250), 5333
# 3D noise defining structure of river canyon walls. # 3D noise defining structure of river canyon walls.
mgv7_np_ridge (Ridge noise) noise_params_3d 0, 1, (100, 100, 100), 6467, 4, 0.75, 2.0 mgv7_np_ridge (Ridge noise) noise_params_3d 0, 1, (100, 100, 100), 6467, 4, 0.75, 2.0
# 3D noise defining structure of floatlands.
# If altered from the default, the noise 'scale' (0.7 by default) may need
# to be adjusted, as floatland tapering functions best when this noise has
# a value range of approximately -2.0 to 2.0.
mgv7_np_floatland (Floatland noise) noise_params_3d 0, 0.7, (384, 96, 384), 1009, 4, 0.75, 1.618
# 3D noise defining giant caverns. # 3D noise defining giant caverns.
mgv7_np_cavern (Cavern noise) noise_params_3d 0, 1, (384, 128, 384), 723, 5, 0.63, 2.0 mgv7_np_cavern (Cavern noise) noise_params_3d 0, 1, (384, 128, 384), 723, 5, 0.63, 2.0

@ -1,7 +1,7 @@
/* /*
Minetest Minetest
Copyright (C) 2013-2019 kwolekr, Ryan Kwolek <kwolekr@minetest.net> Copyright (C) 2014-2020 paramat
Copyright (C) 2014-2019 paramat Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by
@ -56,6 +56,12 @@ MapgenV7::MapgenV7(MapgenV7Params *params, EmergeParams *emerge)
{ {
spflags = params->spflags; spflags = params->spflags;
mount_zero_level = params->mount_zero_level; mount_zero_level = params->mount_zero_level;
floatland_ymin = params->floatland_ymin;
floatland_ymax = params->floatland_ymax;
floatland_taper = params->floatland_taper;
float_taper_exp = params->float_taper_exp;
floatland_density = params->floatland_density;
floatland_ywater = params->floatland_ywater;
cave_width = params->cave_width; cave_width = params->cave_width;
large_cave_depth = params->large_cave_depth; large_cave_depth = params->large_cave_depth;
@ -70,6 +76,9 @@ MapgenV7::MapgenV7(MapgenV7Params *params, EmergeParams *emerge)
dungeon_ymin = params->dungeon_ymin; dungeon_ymin = params->dungeon_ymin;
dungeon_ymax = params->dungeon_ymax; dungeon_ymax = params->dungeon_ymax;
// Allocate floatland noise offset cache
this->float_offset_cache = new float[csize.Y + 2];
// 2D noise // 2D noise
noise_terrain_base = noise_terrain_base =
new Noise(&params->np_terrain_base, seed, csize.X, csize.Z); new Noise(&params->np_terrain_base, seed, csize.X, csize.Z);
@ -100,6 +109,12 @@ MapgenV7::MapgenV7(MapgenV7Params *params, EmergeParams *emerge)
new Noise(&params->np_ridge, seed, csize.X, csize.Y + 2, csize.Z); new Noise(&params->np_ridge, seed, csize.X, csize.Y + 2, csize.Z);
} }
if (spflags & MGV7_FLOATLANDS) {
// 3D noise, 1 up, 1 down overgeneration
noise_floatland =
new Noise(&params->np_floatland, seed, csize.X, csize.Y + 2, csize.Z);
}
// 3D noise, 1 down overgeneration // 3D noise, 1 down overgeneration
MapgenBasic::np_cave1 = params->np_cave1; MapgenBasic::np_cave1 = params->np_cave1;
MapgenBasic::np_cave2 = params->np_cave2; MapgenBasic::np_cave2 = params->np_cave2;
@ -126,6 +141,12 @@ MapgenV7::~MapgenV7()
delete noise_ridge_uwater; delete noise_ridge_uwater;
delete noise_ridge; delete noise_ridge;
} }
if (spflags & MGV7_FLOATLANDS) {
delete noise_floatland;
}
delete []float_offset_cache;
} }
@ -139,6 +160,7 @@ MapgenV7Params::MapgenV7Params():
np_ridge_uwater (0.0, 1.0, v3f(1000, 1000, 1000), 85039, 5, 0.6, 2.0), np_ridge_uwater (0.0, 1.0, v3f(1000, 1000, 1000), 85039, 5, 0.6, 2.0),
np_mountain (-0.6, 1.0, v3f(250, 350, 250), 5333, 5, 0.63, 2.0), np_mountain (-0.6, 1.0, v3f(250, 350, 250), 5333, 5, 0.63, 2.0),
np_ridge (0.0, 1.0, v3f(100, 100, 100), 6467, 4, 0.75, 2.0), np_ridge (0.0, 1.0, v3f(100, 100, 100), 6467, 4, 0.75, 2.0),
np_floatland (0.0, 0.7, v3f(384, 96, 384), 1009, 4, 0.75, 1.618),
np_cavern (0.0, 1.0, v3f(384, 128, 384), 723, 5, 0.63, 2.0), np_cavern (0.0, 1.0, v3f(384, 128, 384), 723, 5, 0.63, 2.0),
np_cave1 (0.0, 12.0, v3f(61, 61, 61), 52534, 3, 0.5, 2.0), np_cave1 (0.0, 12.0, v3f(61, 61, 61), 52534, 3, 0.5, 2.0),
np_cave2 (0.0, 12.0, v3f(67, 67, 67), 10325, 3, 0.5, 2.0), np_cave2 (0.0, 12.0, v3f(67, 67, 67), 10325, 3, 0.5, 2.0),
@ -151,6 +173,13 @@ void MapgenV7Params::readParams(const Settings *settings)
{ {
settings->getFlagStrNoEx("mgv7_spflags", spflags, flagdesc_mapgen_v7); settings->getFlagStrNoEx("mgv7_spflags", spflags, flagdesc_mapgen_v7);
settings->getS16NoEx("mgv7_mount_zero_level", mount_zero_level); settings->getS16NoEx("mgv7_mount_zero_level", mount_zero_level);
settings->getS16NoEx("mgv7_floatland_ymin", floatland_ymin);
settings->getS16NoEx("mgv7_floatland_ymax", floatland_ymax);
settings->getS16NoEx("mgv7_floatland_taper", floatland_taper);
settings->getFloatNoEx("mgv7_float_taper_exp", float_taper_exp);
settings->getFloatNoEx("mgv7_floatland_density", floatland_density);
settings->getS16NoEx("mgv7_floatland_ywater", floatland_ywater);
settings->getFloatNoEx("mgv7_cave_width", cave_width); settings->getFloatNoEx("mgv7_cave_width", cave_width);
settings->getS16NoEx("mgv7_large_cave_depth", large_cave_depth); settings->getS16NoEx("mgv7_large_cave_depth", large_cave_depth);
settings->getU16NoEx("mgv7_small_cave_num_min", small_cave_num_min); settings->getU16NoEx("mgv7_small_cave_num_min", small_cave_num_min);
@ -173,6 +202,7 @@ void MapgenV7Params::readParams(const Settings *settings)
settings->getNoiseParams("mgv7_np_ridge_uwater", np_ridge_uwater); settings->getNoiseParams("mgv7_np_ridge_uwater", np_ridge_uwater);
settings->getNoiseParams("mgv7_np_mountain", np_mountain); settings->getNoiseParams("mgv7_np_mountain", np_mountain);
settings->getNoiseParams("mgv7_np_ridge", np_ridge); settings->getNoiseParams("mgv7_np_ridge", np_ridge);
settings->getNoiseParams("mgv7_np_floatland", np_floatland);
settings->getNoiseParams("mgv7_np_cavern", np_cavern); settings->getNoiseParams("mgv7_np_cavern", np_cavern);
settings->getNoiseParams("mgv7_np_cave1", np_cave1); settings->getNoiseParams("mgv7_np_cave1", np_cave1);
settings->getNoiseParams("mgv7_np_cave2", np_cave2); settings->getNoiseParams("mgv7_np_cave2", np_cave2);
@ -184,6 +214,13 @@ void MapgenV7Params::writeParams(Settings *settings) const
{ {
settings->setFlagStr("mgv7_spflags", spflags, flagdesc_mapgen_v7); settings->setFlagStr("mgv7_spflags", spflags, flagdesc_mapgen_v7);
settings->setS16("mgv7_mount_zero_level", mount_zero_level); settings->setS16("mgv7_mount_zero_level", mount_zero_level);
settings->setS16("mgv7_floatland_ymin", floatland_ymin);
settings->setS16("mgv7_floatland_ymax", floatland_ymax);
settings->setS16("mgv7_floatland_taper", floatland_taper);
settings->setFloat("mgv7_float_taper_exp", float_taper_exp);
settings->setFloat("mgv7_floatland_density", floatland_density);
settings->setS16("mgv7_floatland_ywater", floatland_ywater);
settings->setFloat("mgv7_cave_width", cave_width); settings->setFloat("mgv7_cave_width", cave_width);
settings->setS16("mgv7_large_cave_depth", large_cave_depth); settings->setS16("mgv7_large_cave_depth", large_cave_depth);
settings->setU16("mgv7_small_cave_num_min", small_cave_num_min); settings->setU16("mgv7_small_cave_num_min", small_cave_num_min);
@ -206,6 +243,7 @@ void MapgenV7Params::writeParams(Settings *settings) const
settings->setNoiseParams("mgv7_np_ridge_uwater", np_ridge_uwater); settings->setNoiseParams("mgv7_np_ridge_uwater", np_ridge_uwater);
settings->setNoiseParams("mgv7_np_mountain", np_mountain); settings->setNoiseParams("mgv7_np_mountain", np_mountain);
settings->setNoiseParams("mgv7_np_ridge", np_ridge); settings->setNoiseParams("mgv7_np_ridge", np_ridge);
settings->setNoiseParams("mgv7_np_floatland", np_floatland);
settings->setNoiseParams("mgv7_np_cavern", np_cavern); settings->setNoiseParams("mgv7_np_cavern", np_cavern);
settings->setNoiseParams("mgv7_np_cave1", np_cave1); settings->setNoiseParams("mgv7_np_cave1", np_cave1);
settings->setNoiseParams("mgv7_np_cave2", np_cave2); settings->setNoiseParams("mgv7_np_cave2", np_cave2);
@ -357,8 +395,9 @@ void MapgenV7::makeChunk(BlockMakeData *data)
updateLiquid(&data->transforming_liquid, full_node_min, full_node_max); updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
// Calculate lighting // Calculate lighting
// TODO disable in and just below floatlands // Limit floatland shadows
bool propagate_shadow = true; bool propagate_shadow = !((spflags & MGV7_FLOATLANDS) &&
node_max.Y >= floatland_ymin - csize.Y * 2 && node_min.Y <= floatland_ymax);
if (flags & MG_LIGHT) if (flags & MG_LIGHT)
calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0), calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
@ -427,6 +466,12 @@ bool MapgenV7::getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y)
} }
bool MapgenV7::getFloatlandTerrainFromMap(int idx_xyz, float float_offset)
{
return noise_floatland->result[idx_xyz] + floatland_density - float_offset >= 0.0f;
}
int MapgenV7::generateTerrain() int MapgenV7::generateTerrain()
{ {
MapNode n_air(CONTENT_AIR); MapNode n_air(CONTENT_AIR);
@ -446,6 +491,35 @@ int MapgenV7::generateTerrain()
noise_mountain->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); noise_mountain->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
} }
//// Floatlands
// 'Generate floatlands in this mapchunk' bool for
// simplification of condition checks in y-loop.
bool gen_floatlands = false;
u8 cache_index = 0;
// Y values where floatland tapering starts
s16 float_taper_ymax = floatland_ymax - floatland_taper;
s16 float_taper_ymin = floatland_ymin + floatland_taper;
if ((spflags & MGV7_FLOATLANDS) &&
node_max.Y >= floatland_ymin && node_min.Y <= floatland_ymax) {
gen_floatlands = true;
// Calculate noise for floatland generation
noise_floatland->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
// Cache floatland noise offset values, for floatland tapering
for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++, cache_index++) {
float float_offset = 0.0f;
if (y > float_taper_ymax) {
float_offset = std::pow((y - float_taper_ymax) / (float)floatland_taper,
float_taper_exp) * 4.0f;
} else if (y < float_taper_ymin) {
float_offset = std::pow((float_taper_ymin - y) / (float)floatland_taper,
float_taper_exp) * 4.0f;
}
float_offset_cache[cache_index] = float_offset;
}
}
//// Place nodes //// Place nodes
const v3s16 &em = vm->m_area.getExtent(); const v3s16 &em = vm->m_area.getExtent();
s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT; s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
@ -457,13 +531,15 @@ int MapgenV7::generateTerrain()
if (surface_y > stone_surface_max_y) if (surface_y > stone_surface_max_y)
stone_surface_max_y = surface_y; stone_surface_max_y = surface_y;
cache_index = 0;
u32 vi = vm->m_area.index(x, node_min.Y - 1, z); u32 vi = vm->m_area.index(x, node_min.Y - 1, z);
u32 index3d = (z - node_min.Z) * zstride_1u1d + (x - node_min.X); u32 index3d = (z - node_min.Z) * zstride_1u1d + (x - node_min.X);
for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; for (s16 y = node_min.Y - 1; y <= node_max.Y + 1;
y++, y++,
index3d += ystride, index3d += ystride,
VoxelArea::add_y(em, vi, 1)) { VoxelArea::add_y(em, vi, 1),
cache_index++) {
if (vm->m_data[vi].getContent() != CONTENT_IGNORE) if (vm->m_data[vi].getContent() != CONTENT_IGNORE)
continue; continue;
@ -474,10 +550,18 @@ int MapgenV7::generateTerrain()
vm->m_data[vi] = n_stone; // Mountain terrain vm->m_data[vi] = n_stone; // Mountain terrain
if (y > stone_surface_max_y) if (y > stone_surface_max_y)
stone_surface_max_y = y; stone_surface_max_y = y;
} else if (y <= water_level) { } else if (gen_floatlands &&
getFloatlandTerrainFromMap(index3d,
float_offset_cache[cache_index])) {
vm->m_data[vi] = n_stone; // Floatland terrain
if (y > stone_surface_max_y)
stone_surface_max_y = y;
} else if (y <= water_level) { // Surface water
vm->m_data[vi] = n_water; vm->m_data[vi] = n_water;
} else if (gen_floatlands && y >= float_taper_ymax && y <= floatland_ywater) {
vm->m_data[vi] = n_water; // Water for solid floatland layer only
} else { } else {
vm->m_data[vi] = n_air; vm->m_data[vi] = n_air; // Air
} }
} }
} }
@ -488,8 +572,8 @@ int MapgenV7::generateTerrain()
void MapgenV7::generateRidgeTerrain() void MapgenV7::generateRidgeTerrain()
{ {
// TODO disable river canyons in floatlands if (node_max.Y < water_level - 16 ||
if (node_max.Y < water_level - 16) (node_max.Y >= floatland_ymin && node_min.Y <= floatland_ymax))
return; return;
noise_ridge->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); noise_ridge->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);

@ -1,7 +1,7 @@
/* /*
Minetest Minetest
Copyright (C) 2013-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net> Copyright (C) 2014-2020 paramat
Copyright (C) 2014-2018 paramat Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by
@ -36,6 +36,12 @@ extern FlagDesc flagdesc_mapgen_v7[];
struct MapgenV7Params : public MapgenParams { struct MapgenV7Params : public MapgenParams {
s16 mount_zero_level = 0; s16 mount_zero_level = 0;
s16 floatland_ymin = 1024;
s16 floatland_ymax = 4096;
s16 floatland_taper = 256;
float float_taper_exp = 2.0f;
float floatland_density = -0.6f;
s16 floatland_ywater = -31000;
float cave_width = 0.09f; float cave_width = 0.09f;
s16 large_cave_depth = -33; s16 large_cave_depth = -33;
@ -59,6 +65,7 @@ struct MapgenV7Params : public MapgenParams {
NoiseParams np_ridge_uwater; NoiseParams np_ridge_uwater;
NoiseParams np_mountain; NoiseParams np_mountain;
NoiseParams np_ridge; NoiseParams np_ridge;
NoiseParams np_floatland;
NoiseParams np_cavern; NoiseParams np_cavern;
NoiseParams np_cave1; NoiseParams np_cave1;
NoiseParams np_cave2; NoiseParams np_cave2;
@ -87,12 +94,21 @@ public:
float baseTerrainLevelFromMap(int index); float baseTerrainLevelFromMap(int index);
bool getMountainTerrainAtPoint(s16 x, s16 y, s16 z); bool getMountainTerrainAtPoint(s16 x, s16 y, s16 z);
bool getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y); bool getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y);
bool getFloatlandTerrainFromMap(int idx_xyz, float float_offset);
int generateTerrain(); int generateTerrain();
void generateRidgeTerrain(); void generateRidgeTerrain();
private: private:
s16 mount_zero_level; s16 mount_zero_level;
s16 floatland_ymin;
s16 floatland_ymax;
s16 floatland_taper;
float float_taper_exp;
float floatland_density;
s16 floatland_ywater;
float *float_offset_cache = nullptr;
Noise *noise_terrain_base; Noise *noise_terrain_base;
Noise *noise_terrain_alt; Noise *noise_terrain_alt;
@ -102,4 +118,5 @@ private:
Noise *noise_ridge_uwater; Noise *noise_ridge_uwater;
Noise *noise_mountain; Noise *noise_mountain;
Noise *noise_ridge; Noise *noise_ridge;
Noise *noise_floatland;
}; };