Mgv5/v7: Fix generateBiomes biome recalculation logic Biomegen down to y = -192 for...
authorparamat <mat.gregory@virginmedia.com>
Thu, 21 May 2015 02:20:41 +0000 (03:20 +0100)
committerparamat <mat.gregory@virginmedia.com>
Sat, 23 May 2015 01:18:30 +0000 (02:18 +0100)
src/mapgen_v5.cpp
src/mapgen_v5.h
src/mapgen_v7.cpp
src/mapgen_v7.h

index 54fc137458a39454618b1f4ec5157c4378746f03..abea2f7142ce3b693927e2d5000056b5fd78e203 100644 (file)
@@ -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();
-
-                       if (c != CONTENT_IGNORE && c != CONTENT_AIR &&
-                                       (y == node_max.Y || have_air)) {
+                       content_t c = vm->m_data[vi].getContent();
+
+                       // 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_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 (c == c_stone) {
+                               content_t c_below = vm->m_data[vi - em.X].getContent();
+
+                               // 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);
                }
        }
 
index 5575dfe61a45f484ed0ff6ac9858acab53eef476..e8455a46f6fcb1d64b3d162d25ce2edfd6954083 100644 (file)
@@ -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
index 1733fff49ac44329dde88d67f285a57fb7724a8f..4833699f811ada343997af6fd77b92c72ba6bafb 100644 (file)
@@ -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_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 (c == c_stone) {
+                               content_t c_below = vm->m_data[vi - em.X].getContent();
+
+                               // 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);
                }
        }
 
index eb46c371b2683d6cf0cc9284681612f8b2430ead..84f0c9efad05dff2e03520f94984ab24a137e3ff 100644 (file)
@@ -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