Cleanup in content_mapblock (#5746)
[oweals/minetest.git] / src / cavegen.cpp
index 5f0af969969e0cbec5ec0b4d31063aded44c7a9e..dbed7995111dc5c4a614749f32763bd1f91cae65 100644 (file)
@@ -23,9 +23,230 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mapgen_v5.h"
 #include "mapgen_v6.h"
 #include "mapgen_v7.h"
+#include "mg_biome.h"
 #include "cavegen.h"
 
-NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0);
+static NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0);
+
+
+////
+//// CavesNoiseIntersection
+////
+
+CavesNoiseIntersection::CavesNoiseIntersection(
+       INodeDefManager *nodedef, BiomeManager *biomemgr, v3s16 chunksize,
+       NoiseParams *np_cave1, NoiseParams *np_cave2, s32 seed, float cave_width)
+{
+       assert(nodedef);
+       assert(biomemgr);
+
+       m_ndef = nodedef;
+       m_bmgr = biomemgr;
+
+       m_csize = chunksize;
+       m_cave_width = cave_width;
+
+       m_ystride    = m_csize.X;
+       m_zstride_1d = m_csize.X * (m_csize.Y + 1);
+
+       // Noises are created using 1-down overgeneration
+       // A Nx-by-1-by-Nz-sized plane is at the bottom of the desired for
+       // re-carving the solid overtop placed for blocking sunlight
+       noise_cave1 = new Noise(np_cave1, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
+       noise_cave2 = new Noise(np_cave2, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
+}
+
+
+CavesNoiseIntersection::~CavesNoiseIntersection()
+{
+       delete noise_cave1;
+       delete noise_cave2;
+}
+
+
+void CavesNoiseIntersection::generateCaves(MMVManip *vm,
+       v3s16 nmin, v3s16 nmax, u8 *biomemap)
+{
+       assert(vm);
+       assert(biomemap);
+
+       noise_cave1->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
+       noise_cave2->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
+
+       v3s16 em = vm->m_area.getExtent();
+       u32 index2d = 0;  // Biomemap index
+
+       for (s16 z = nmin.Z; z <= nmax.Z; z++)
+       for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) {
+               bool column_is_open = false;  // Is column open to overground
+               bool is_under_river = false;  // Is column under river water
+               bool is_under_tunnel = false;  // Is tunnel or is under tunnel
+               // Indexes at column top
+               u32 vi = vm->m_area.index(x, nmax.Y, z);
+               u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride +
+                       (x - nmin.X);  // 3D noise index
+               // Biome of column
+               Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index2d]);
+               u16 depth_top = biome->depth_top;
+               u16 base_filler = depth_top + biome->depth_filler;
+               u16 depth_riverbed = biome->depth_riverbed;
+               u16 nplaced = 0;
+               // Don't excavate the overgenerated stone at nmax.Y + 1,
+               // this creates a 'roof' over the tunnel, preventing light in
+               // tunnels at mapchunk borders when generating mapchunks upwards.
+               // This 'roof' is removed when the mapchunk above is generated.
+               for (s16 y = nmax.Y; y >= nmin.Y - 1; y--,
+                               index3d -= m_ystride,
+                               vm->m_area.add_y(em, vi, -1)) {
+
+                       content_t c = vm->m_data[vi].getContent();
+                       if (c == CONTENT_AIR || c == biome->c_water_top ||
+                                       c == biome->c_water) {
+                               column_is_open = true;
+                               continue;
+                       } else if (c == biome->c_river_water) {
+                               column_is_open = true;
+                               is_under_river = true;
+                               continue;
+                       }
+                       // Ground
+                       float d1 = contour(noise_cave1->result[index3d]);
+                       float d2 = contour(noise_cave2->result[index3d]);
+
+                       if (d1 * d2 > m_cave_width && m_ndef->get(c).is_ground_content) {
+                               // In tunnel and ground content, excavate
+                               vm->m_data[vi] = MapNode(CONTENT_AIR);
+                               is_under_tunnel = true;
+                       } else if (column_is_open && is_under_tunnel &&
+                                       (c == biome->c_stone || c == biome->c_filler)) {
+                               // Tunnel entrance floor, place biome surface nodes
+                               if (is_under_river) {
+                                       if (nplaced < depth_riverbed) {
+                                               vm->m_data[vi] = MapNode(biome->c_riverbed);
+                                               nplaced++;
+                                       } else {
+                                               // Disable top/filler placement
+                                               column_is_open = false;
+                                               is_under_river = false;
+                                               is_under_tunnel = false;
+                                       }
+                               } else 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 {
+                                       // Disable top/filler placement
+                                       column_is_open = false;
+                                       is_under_tunnel = false;
+                               }
+                       } else {
+                               // Not tunnel or tunnel entrance floor
+                               column_is_open = false;
+                       }
+               }
+       }
+}
+
+
+////
+//// CavernsNoise
+////
+
+CavernsNoise::CavernsNoise(
+       INodeDefManager *nodedef, v3s16 chunksize, NoiseParams *np_cavern,
+       s32 seed, float cavern_limit, float cavern_taper, float cavern_threshold)
+{
+       assert(nodedef);
+
+       m_ndef  = nodedef;
+
+       m_csize            = chunksize;
+       m_cavern_limit     = cavern_limit;
+       m_cavern_taper     = cavern_taper;
+       m_cavern_threshold = cavern_threshold;
+
+       m_ystride = m_csize.X;
+       m_zstride_1d = m_csize.X * (m_csize.Y + 1);
+
+       // Noise is created using 1-down overgeneration
+       // A Nx-by-1-by-Nz-sized plane is at the bottom of the desired for
+       // re-carving the solid overtop placed for blocking sunlight
+       noise_cavern = new Noise(np_cavern, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
+
+       c_water_source = m_ndef->getId("mapgen_water_source");
+       if (c_water_source == CONTENT_IGNORE)
+               c_water_source = CONTENT_AIR;
+
+       c_lava_source = m_ndef->getId("mapgen_lava_source");
+       if (c_lava_source == CONTENT_IGNORE)
+               c_lava_source = CONTENT_AIR;
+}
+
+
+CavernsNoise::~CavernsNoise()
+{
+       delete noise_cavern;
+}
+
+
+bool CavernsNoise::generateCaverns(MMVManip *vm, v3s16 nmin, v3s16 nmax)
+{
+       assert(vm);
+
+       // Calculate noise
+       noise_cavern->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
+
+       // Cache cavern_amp values
+       float *cavern_amp = new float[m_csize.Y + 1];
+       u8 cavern_amp_index = 0;  // Index zero at column top
+       for (s16 y = nmax.Y; y >= nmin.Y - 1; y--, cavern_amp_index++) {
+               cavern_amp[cavern_amp_index] =
+                       MYMIN((m_cavern_limit - y) / (float)m_cavern_taper, 1.0f);
+       }
+
+       //// Place nodes
+       bool near_cavern = false;
+       v3s16 em = vm->m_area.getExtent();
+       u32 index2d = 0;
+
+       for (s16 z = nmin.Z; z <= nmax.Z; z++)
+       for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) {
+               // Reset cave_amp index to column top
+               cavern_amp_index = 0;
+               // Initial voxelmanip index at column top
+               u32 vi = vm->m_area.index(x, nmax.Y, z);
+               // Initial 3D noise index at column top
+               u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride +
+                       (x - nmin.X);
+               // Don't excavate the overgenerated stone at node_max.Y + 1,
+               // this creates a 'roof' over the cavern, preventing light in
+               // caverns at mapchunk borders when generating mapchunks upwards.
+               // This 'roof' is excavated when the mapchunk above is generated.
+               for (s16 y = nmax.Y; y >= nmin.Y - 1; y--,
+                               index3d -= m_ystride,
+                               vm->m_area.add_y(em, vi, -1),
+                               cavern_amp_index++) {
+                       content_t c = vm->m_data[vi].getContent();
+                       float n_absamp_cavern = fabs(noise_cavern->result[index3d]) *
+                               cavern_amp[cavern_amp_index];
+                       // Disable CavesRandomWalk at a safe distance from caverns
+                       // to avoid excessively spreading liquids in caverns.
+                       if (n_absamp_cavern > m_cavern_threshold - 0.1f) {
+                               near_cavern = true;
+                               if (n_absamp_cavern > m_cavern_threshold &&
+                                               m_ndef->get(c).is_ground_content)
+                                       vm->m_data[vi] = MapNode(CONTENT_AIR);
+                       }
+               }
+       }
+
+       delete[] cavern_amp;
+
+       return near_cavern;
+}
+
 
 ////
 //// CavesRandomWalk
@@ -34,7 +255,7 @@ NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0
 CavesRandomWalk::CavesRandomWalk(
        INodeDefManager *ndef,
        GenerateNotifier *gennotify,
-       int seed,
+       s32 seed,
        int water_level,
        content_t water_source,
        content_t lava_source)