diff --git a/src/mapgen_v5.cpp b/src/mapgen_v5.cpp index 54fc13745..abea2f714 100644 --- a/src/mapgen_v5.cpp +++ b/src/mapgen_v5.cpp @@ -327,7 +327,7 @@ void MapgenV5::calculateNoise() noise_cave2->perlinMap3D(x, y, z); } - if (node_max.Y >= water_level) { + if (node_max.Y >= BIOMEGEN_BASE_V5) { noise_filler_depth->perlinMap2D(x, z); noise_heat->perlinMap2D(x, z); noise_humidity->perlinMap2D(x, z); @@ -396,7 +396,7 @@ int MapgenV5::generateBaseTerrain() MgStoneType MapgenV5::generateBiomes(float *heat_map, float *humidity_map) { - if (node_max.Y < water_level) + if (node_max.Y < BIOMEGEN_BASE_V5) return STONE; v3s16 em = vm->m_area.getExtent(); @@ -406,74 +406,80 @@ MgStoneType MapgenV5::generateBiomes(float *heat_map, float *humidity_map) for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, index++) { Biome *biome = NULL; - s16 dfiller = 0; - s16 y0_top = 0; - s16 y0_filler = 0; - s16 depth_water_top = 0; + u16 depth_top = 0; + u16 base_filler = 0; + u16 depth_water_top = 0; + u32 vi = vm->m_area.index(x, node_max.Y, z); - s16 nplaced = 0; - u32 i = vm->m_area.index(x, node_max.Y, z); + // Check node at base of mapchunk above, either a node of a previously + // generated mapchunk or if not, a node of overgenerated base terrain. + content_t c_above = vm->m_data[vi + em.X].getContent(); + bool air_above = c_above == CONTENT_AIR; - content_t c_above = vm->m_data[i + em.X].getContent(); - bool have_air = c_above == CONTENT_AIR; + // If there is air above enable top/filler placement, otherwise force nplaced to + // stone level by setting a number that will exceed any possible filler depth. + u16 nplaced = (air_above) ? 0 : (u16)-1; for (s16 y = node_max.Y; y >= node_min.Y; y--) { - content_t c = vm->m_data[i].getContent(); + content_t c = vm->m_data[vi].getContent(); - if (c != CONTENT_IGNORE && c != CONTENT_AIR && - (y == node_max.Y || have_air)) { + // Biome is only (re)calculated for each stone/water upper surface found + // below air while working downwards. The chosen biome then remains in + // effect for all nodes below until the next biome recalculation. + // Biome is (re)calculated when a stone/water node is either: detected + // below an air node, or, is at column top and might be underground + // or underwater and therefore might not be below air. + if (c != CONTENT_AIR && (y == node_max.Y || air_above)) { biome = bmgr->getBiome(heat_map[index], humidity_map[index], y); - dfiller = biome->depth_filler + noise_filler_depth->result[index]; - y0_top = biome->depth_top; - y0_filler = biome->depth_top + dfiller; + depth_top = biome->depth_top; + base_filler = MYMAX(depth_top + biome->depth_filler + + noise_filler_depth->result[index], 0); depth_water_top = biome->depth_water_top; + // Detect stone type for dungeons during every biome calculation. + // This is more efficient than detecting per-node and will not + // miss any desert stone or sandstone biomes. if (biome->c_stone == c_desert_stone) stone_type = DESERT_STONE; else if (biome->c_stone == c_sandstone) stone_type = SANDSTONE; } - if (c == c_stone && have_air) { - content_t c_below = vm->m_data[i - em.X].getContent(); + if (c == c_stone) { + content_t c_below = vm->m_data[vi - em.X].getContent(); - if (c_below != CONTENT_AIR && c_below != c_water_source) { - if (nplaced < y0_top) { - vm->m_data[i] = MapNode(biome->c_top); - nplaced++; - } else if (nplaced < y0_filler && nplaced >= y0_top) { - vm->m_data[i] = MapNode(biome->c_filler); - nplaced++; - } else if (c == c_stone) { - have_air = false; - nplaced = 0; - vm->m_data[i] = MapNode(biome->c_stone); - } else { - have_air = false; - nplaced = 0; - } - } else if (c == c_stone) { - have_air = false; - nplaced = 0; - vm->m_data[i] = MapNode(biome->c_stone); + // If the node below isn't solid, make this node stone, so that + // any top/filler nodes above are structurally supported. + // This is done by aborting the cycle of top/filler placement + // immediately by forcing nplaced to stone level. + if (c_below == CONTENT_AIR || c_below == c_water_source) + nplaced = (u16)-1; + + if (nplaced < depth_top) { + vm->m_data[vi] = MapNode(biome->c_top); + nplaced++; + } else if (nplaced < base_filler) { + vm->m_data[vi] = MapNode(biome->c_filler); + nplaced++; + } else { + vm->m_data[vi] = MapNode(biome->c_stone); } - } else if (c == c_stone) { - have_air = false; - nplaced = 0; - vm->m_data[i] = MapNode(biome->c_stone); + + air_above = false; } else if (c == c_water_source) { - have_air = true; - nplaced = 0; - if (y > water_level - depth_water_top) - vm->m_data[i] = MapNode(biome->c_water_top); - else - vm->m_data[i] = MapNode(biome->c_water); + vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top)) ? + biome->c_water_top : biome->c_water); + nplaced = 0; // Enable top/filler placement for next surface + air_above = false; // Biome is not recalculated underwater } else if (c == CONTENT_AIR) { - have_air = true; - nplaced = 0; + nplaced = 0; // Enable top/filler placement for next surface + air_above = true; // Biome will be recalculated at next surface + } else { // Possible various nodes overgenerated from neighbouring mapchunks + nplaced = (u16)-1; // Disable top/filler placement + air_above = false; } - vm->m_area.add_y(em, i, -1); + vm->m_area.add_y(em, vi, -1); } } diff --git a/src/mapgen_v5.h b/src/mapgen_v5.h index 5575dfe61..e8455a46f 100644 --- a/src/mapgen_v5.h +++ b/src/mapgen_v5.h @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapgen.h" #define LARGE_CAVE_DEPTH -256 +#define BIOMEGEN_BASE_V5 -192 /////////////////// Mapgen V5 flags //#define MGV5_ 0x01 diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index 1733fff49..4833699f8 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -362,7 +362,7 @@ void MapgenV7::calculateNoise() noise_mount_height->perlinMap2D(x, z); } - if (node_max.Y >= water_level) { + if (node_max.Y >= BIOMEGEN_BASE_V7) { noise_filler_depth->perlinMap2D(x, z); noise_heat->perlinMap2D(x, z); noise_humidity->perlinMap2D(x, z); @@ -591,7 +591,7 @@ void MapgenV7::generateRidgeTerrain() MgStoneType MapgenV7::generateBiomes(float *heat_map, float *humidity_map) { - if (node_max.Y < water_level) + if (node_max.Y < BIOMEGEN_BASE_V7) return STONE; v3s16 em = vm->m_area.getExtent(); @@ -601,74 +601,80 @@ MgStoneType MapgenV7::generateBiomes(float *heat_map, float *humidity_map) for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, index++) { Biome *biome = NULL; - s16 dfiller = 0; - s16 y0_top = 0; - s16 y0_filler = 0; - s16 depth_water_top = 0; + u16 depth_top = 0; + u16 base_filler = 0; + u16 depth_water_top = 0; + u32 vi = vm->m_area.index(x, node_max.Y, z); - s16 nplaced = 0; - u32 i = vm->m_area.index(x, node_max.Y, z); + // Check node at base of mapchunk above, either a node of a previously + // generated mapchunk or if not, a node of overgenerated base terrain. + content_t c_above = vm->m_data[vi + em.X].getContent(); + bool air_above = c_above == CONTENT_AIR; - content_t c_above = vm->m_data[i + em.X].getContent(); - bool have_air = c_above == CONTENT_AIR; + // If there is air above enable top/filler placement, otherwise force nplaced to + // stone level by setting a number that will exceed any possible filler depth. + u16 nplaced = (air_above) ? 0 : (u16)-1; for (s16 y = node_max.Y; y >= node_min.Y; y--) { - content_t c = vm->m_data[i].getContent(); + content_t c = vm->m_data[vi].getContent(); - if (c != CONTENT_IGNORE && c != CONTENT_AIR && - (y == node_max.Y || have_air)) { + // Biome is only (re)calculated for each stone/water upper surface found + // below air while working downwards. The chosen biome then remains in + // effect for all nodes below until the next biome recalculation. + // Biome is (re)calculated when a stone/water node is either: detected + // below an air node, or, is at column top and might be underground + // or underwater and therefore might not be below air. + if (c != CONTENT_AIR && (y == node_max.Y || air_above)) { biome = bmgr->getBiome(heat_map[index], humidity_map[index], y); - dfiller = biome->depth_filler + noise_filler_depth->result[index]; - y0_top = biome->depth_top; - y0_filler = biome->depth_top + dfiller; + depth_top = biome->depth_top; + base_filler = MYMAX(depth_top + biome->depth_filler + + noise_filler_depth->result[index], 0); depth_water_top = biome->depth_water_top; + // Detect stone type for dungeons during every biome calculation. + // This is more efficient than detecting per-node and will not + // miss any desert stone or sandstone biomes. if (biome->c_stone == c_desert_stone) stone_type = DESERT_STONE; else if (biome->c_stone == c_sandstone) stone_type = SANDSTONE; } - if (c == c_stone && have_air) { - content_t c_below = vm->m_data[i - em.X].getContent(); + if (c == c_stone) { + content_t c_below = vm->m_data[vi - em.X].getContent(); - if (c_below != CONTENT_AIR && c_below != c_water_source) { - if (nplaced < y0_top) { - vm->m_data[i] = MapNode(biome->c_top); - nplaced++; - } else if (nplaced < y0_filler && nplaced >= y0_top) { - vm->m_data[i] = MapNode(biome->c_filler); - nplaced++; - } else if (c == c_stone) { - have_air = false; - nplaced = 0; - vm->m_data[i] = MapNode(biome->c_stone); - } else { - have_air = false; - nplaced = 0; - } - } else if (c == c_stone) { - have_air = false; - nplaced = 0; - vm->m_data[i] = MapNode(biome->c_stone); + // If the node below isn't solid, make this node stone, so that + // any top/filler nodes above are structurally supported. + // This is done by aborting the cycle of top/filler placement + // immediately by forcing nplaced to stone level. + if (c_below == CONTENT_AIR || c_below == c_water_source) + nplaced = (u16)-1; + + if (nplaced < depth_top) { + vm->m_data[vi] = MapNode(biome->c_top); + nplaced++; + } else if (nplaced < base_filler) { + vm->m_data[vi] = MapNode(biome->c_filler); + nplaced++; + } else { + vm->m_data[vi] = MapNode(biome->c_stone); } - } else if (c == c_stone) { - have_air = false; - nplaced = 0; - vm->m_data[i] = MapNode(biome->c_stone); + + air_above = false; } else if (c == c_water_source) { - have_air = true; - nplaced = 0; - if (y > water_level - depth_water_top) - vm->m_data[i] = MapNode(biome->c_water_top); - else - vm->m_data[i] = MapNode(biome->c_water); + vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top)) ? + biome->c_water_top : biome->c_water); + nplaced = 0; // Enable top/filler placement for next surface + air_above = false; // Biome is not recalculated underwater } else if (c == CONTENT_AIR) { - have_air = true; - nplaced = 0; + nplaced = 0; // Enable top/filler placement for next surface + air_above = true; // Biome will be recalculated at next surface + } else { // Possible various nodes overgenerated from neighbouring mapchunks + nplaced = (u16)-1; // Disable top/filler placement + air_above = false; } - vm->m_area.add_y(em, i, -1); + vm->m_area.add_y(em, vi, -1); } } diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h index eb46c371b..84f0c9efa 100644 --- a/src/mapgen_v7.h +++ b/src/mapgen_v7.h @@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapgen.h" +#define BIOMEGEN_BASE_V7 -192 + /////////////////// Mapgen V7 flags #define MGV7_MOUNTAINS 0x01 #define MGV7_RIDGES 0x02