From af0f7ac4a2032780eb731918c8fe9dc9e1262b5f Mon Sep 17 00:00:00 2001 From: Paramat Date: Thu, 14 May 2020 22:27:54 +0100 Subject: [PATCH] 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. --- builtin/settingtypes.txt | 49 ++++++++++++++++++- src/mapgen/mapgen_v7.cpp | 102 +++++++++++++++++++++++++++++++++++---- src/mapgen/mapgen_v7.h | 21 +++++++- 3 files changed, 160 insertions(+), 12 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index b75bf2de5..d3f2c60b5 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -1607,12 +1607,53 @@ mgv6_np_apple_trees (Apple trees noise) noise_params_2d 0, 1, (100, 100, 100), 3 [*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 # Y of mountain density gradient zero level. Used to shift mountains vertically. 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. # Value >= 10.0 completely disables generation of tunnels and avoids the # 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. 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. mgv7_np_cavern (Cavern noise) noise_params_3d 0, 1, (384, 128, 384), 723, 5, 0.63, 2.0 diff --git a/src/mapgen/mapgen_v7.cpp b/src/mapgen/mapgen_v7.cpp index 43d5d822f..e93dc9140 100644 --- a/src/mapgen/mapgen_v7.cpp +++ b/src/mapgen/mapgen_v7.cpp @@ -1,7 +1,7 @@ /* Minetest -Copyright (C) 2013-2019 kwolekr, Ryan Kwolek -Copyright (C) 2014-2019 paramat +Copyright (C) 2014-2020 paramat +Copyright (C) 2013-2016 kwolekr, Ryan Kwolek 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 @@ -56,6 +56,12 @@ MapgenV7::MapgenV7(MapgenV7Params *params, EmergeParams *emerge) { spflags = params->spflags; 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; large_cave_depth = params->large_cave_depth; @@ -70,6 +76,9 @@ MapgenV7::MapgenV7(MapgenV7Params *params, EmergeParams *emerge) dungeon_ymin = params->dungeon_ymin; dungeon_ymax = params->dungeon_ymax; + // Allocate floatland noise offset cache + this->float_offset_cache = new float[csize.Y + 2]; + // 2D noise noise_terrain_base = new Noise(¶ms->np_terrain_base, seed, csize.X, csize.Z); @@ -100,6 +109,12 @@ MapgenV7::MapgenV7(MapgenV7Params *params, EmergeParams *emerge) new Noise(¶ms->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(¶ms->np_floatland, seed, csize.X, csize.Y + 2, csize.Z); + } + // 3D noise, 1 down overgeneration MapgenBasic::np_cave1 = params->np_cave1; MapgenBasic::np_cave2 = params->np_cave2; @@ -126,6 +141,12 @@ MapgenV7::~MapgenV7() delete noise_ridge_uwater; 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_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_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_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), @@ -151,6 +173,13 @@ void MapgenV7Params::readParams(const Settings *settings) { settings->getFlagStrNoEx("mgv7_spflags", spflags, flagdesc_mapgen_v7); 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->getS16NoEx("mgv7_large_cave_depth", large_cave_depth); 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_mountain", np_mountain); 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_cave1", np_cave1); 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->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->setS16("mgv7_large_cave_depth", large_cave_depth); 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_mountain", np_mountain); 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_cave1", np_cave1); 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); // Calculate lighting - // TODO disable in and just below floatlands - bool propagate_shadow = true; + // Limit floatland shadows + bool propagate_shadow = !((spflags & MGV7_FLOATLANDS) && + node_max.Y >= floatland_ymin - csize.Y * 2 && node_min.Y <= floatland_ymax); if (flags & MG_LIGHT) 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() { 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); } + //// 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 const v3s16 &em = vm->m_area.getExtent(); s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT; @@ -457,13 +531,15 @@ int MapgenV7::generateTerrain() if (surface_y > stone_surface_max_y) stone_surface_max_y = surface_y; + cache_index = 0; u32 vi = vm->m_area.index(x, node_min.Y - 1, z); u32 index3d = (z - node_min.Z) * zstride_1u1d + (x - node_min.X); for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++, index3d += ystride, - VoxelArea::add_y(em, vi, 1)) { + VoxelArea::add_y(em, vi, 1), + cache_index++) { if (vm->m_data[vi].getContent() != CONTENT_IGNORE) continue; @@ -474,10 +550,18 @@ int MapgenV7::generateTerrain() vm->m_data[vi] = n_stone; // Mountain terrain if (y > stone_surface_max_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; + } else if (gen_floatlands && y >= float_taper_ymax && y <= floatland_ywater) { + vm->m_data[vi] = n_water; // Water for solid floatland layer only } else { - vm->m_data[vi] = n_air; + vm->m_data[vi] = n_air; // Air } } } @@ -488,8 +572,8 @@ int MapgenV7::generateTerrain() 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; noise_ridge->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); diff --git a/src/mapgen/mapgen_v7.h b/src/mapgen/mapgen_v7.h index eeae3a956..4020cd935 100644 --- a/src/mapgen/mapgen_v7.h +++ b/src/mapgen/mapgen_v7.h @@ -1,7 +1,7 @@ /* Minetest -Copyright (C) 2013-2018 kwolekr, Ryan Kwolek -Copyright (C) 2014-2018 paramat +Copyright (C) 2014-2020 paramat +Copyright (C) 2013-2016 kwolekr, Ryan Kwolek 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 @@ -36,6 +36,12 @@ extern FlagDesc flagdesc_mapgen_v7[]; struct MapgenV7Params : public MapgenParams { 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; s16 large_cave_depth = -33; @@ -59,6 +65,7 @@ struct MapgenV7Params : public MapgenParams { NoiseParams np_ridge_uwater; NoiseParams np_mountain; NoiseParams np_ridge; + NoiseParams np_floatland; NoiseParams np_cavern; NoiseParams np_cave1; NoiseParams np_cave2; @@ -87,12 +94,21 @@ public: float baseTerrainLevelFromMap(int index); bool getMountainTerrainAtPoint(s16 x, s16 y, s16 z); bool getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y); + bool getFloatlandTerrainFromMap(int idx_xyz, float float_offset); int generateTerrain(); void generateRidgeTerrain(); private: 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_alt; @@ -102,4 +118,5 @@ private: Noise *noise_ridge_uwater; Noise *noise_mountain; Noise *noise_ridge; + Noise *noise_floatland; }; -- 2.25.1