]> git.lizzy.rs Git - minetest.git/blobdiff - src/mapgen_fractal.cpp
Remove ClientMap::m_camera_mutex
[minetest.git] / src / mapgen_fractal.cpp
index 14dfe5c851b934ca2db2c3c9223b8e9e39eb6f18..8a5c1e2bc8dde8b2d87f80d6c305880f15d6f9a7 100644 (file)
@@ -66,7 +66,7 @@ MapgenFractal::MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager *
        MapgenFractalParams *sp = (MapgenFractalParams *)params->sparams;
        this->spflags = sp->spflags;
 
-       this->formula    = sp->formula;
+       this->fractal    = sp->fractal;
        this->iterations = sp->iterations;
        this->scale      = sp->scale;
        this->offset     = sp->offset;
@@ -77,6 +77,9 @@ MapgenFractal::MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager *
        this->julia_z = sp->julia_z;
        this->julia_w = sp->julia_w;
 
+       this->formula = fractal / 2 + fractal % 2;
+       this->julia   = fractal % 2 == 0;
+
        //// 2D terrain noise
        noise_seabed       = new Noise(&sp->np_seabed, seed, csize.X, csize.Z);
        noise_filler_depth = new Noise(&sp->np_filler_depth, seed, csize.X, csize.Z);
@@ -141,7 +144,7 @@ MapgenFractalParams::MapgenFractalParams()
 {
        spflags = 0;
 
-       formula = 1;
+       fractal = 1;
        iterations = 11;
        scale = v3f(4096.0, 1024.0, 4096.0);
        offset = v3f(1.79, 0.0, 0.0);
@@ -163,7 +166,7 @@ void MapgenFractalParams::readParams(const Settings *settings)
 {
        settings->getFlagStrNoEx("mgfractal_spflags", spflags, flagdesc_mapgen_fractal);
 
-       settings->getU16NoEx("mgfractal_formula", formula);
+       settings->getU16NoEx("mgfractal_fractal", fractal);
        settings->getU16NoEx("mgfractal_iterations", iterations);
        settings->getV3FNoEx("mgfractal_scale", scale);
        settings->getV3FNoEx("mgfractal_offset", offset);
@@ -185,7 +188,7 @@ void MapgenFractalParams::writeParams(Settings *settings) const
 {
        settings->setFlagStr("mgfractal_spflags", spflags, flagdesc_mapgen_fractal, U32_MAX);
 
-       settings->setU16("mgfractal_formula", formula);
+       settings->setU16("mgfractal_fractal", fractal);
        settings->setU16("mgfractal_iterations", iterations);
        settings->setV3F("mgfractal_scale", scale);
        settings->setV3F("mgfractal_offset", offset);
@@ -206,17 +209,28 @@ void MapgenFractalParams::writeParams(Settings *settings) const
 /////////////////////////////////////////////////////////////////
 
 
-int MapgenFractal::getGroundLevelAtPoint(v2s16 p)
+int MapgenFractal::getSpawnLevelAtPoint(v2s16 p)
 {
-       s16 search_start = 128;
-       s16 search_end = -128;
-
-       for (s16 y = search_start; y >= search_end; y--) {
-               if (getFractalAtPoint(p.X, y, p.Y))
-                       return y;
+       bool solid_below = false;  // Dry solid node is present below to spawn on
+       u8 air_count = 0;  // Consecutive air nodes above the dry solid node
+       s16 seabed_level = NoisePerlin2D(&noise_seabed->np, p.X, p.Y, seed);
+       // Seabed can rise above water_level or might be raised to create dry land
+       s16 search_start = MYMAX(seabed_level, water_level + 1);
+       if (seabed_level > water_level)
+               solid_below = true;
+               
+       for (s16 y = search_start; y <= search_start + 128; y++) {
+               if (getFractalAtPoint(p.X, y, p.Y)) {  // Fractal node
+                       solid_below = true;
+                       air_count = 0;
+               } else if (solid_below) {  // Air above solid node
+                       air_count++;
+                       if (air_count == 2)
+                               return y - 2;
+               }
        }
 
-       return -MAX_MAP_GENERATION_LIMIT;
+       return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
 }
 
 
@@ -336,18 +350,15 @@ void MapgenFractal::makeChunk(BlockMakeData *data)
 void MapgenFractal::calculateNoise()
 {
        //TimeTaker t("calculateNoise", NULL, PRECISION_MICRO);
-       int x = node_min.X;
-       int y = node_min.Y - 1;
-       int z = node_min.Z;
+       s16 x = node_min.X;
+       s16 z = node_min.Z;
 
        noise_seabed->perlinMap2D(x, z);
-       noise_filler_depth->perlinMap2D(x, z);
 
-       if (flags & MG_CAVES) {
-               noise_cave1->perlinMap3D(x, y, z);
-               noise_cave2->perlinMap3D(x, y, z);
-       }
+       // Cave noises are calculated in generateCaves()
+       // only if solid terrain is present in mapchunk
 
+       noise_filler_depth->perlinMap2D(x, z);
        noise_heat->perlinMap2D(x, z);
        noise_humidity->perlinMap2D(x, z);
        noise_heat_blend->perlinMap2D(x, z);
@@ -368,7 +379,7 @@ bool MapgenFractal::getFractalAtPoint(s16 x, s16 y, s16 z)
 {
        float cx, cy, cz, cw, ox, oy, oz, ow;
 
-       if (formula % 2 == 0) {  // Julia sets, formula = 2, 4, 6, 8
+       if (julia) {  // Julia set
                cx = julia_x;
                cy = julia_y;
                cz = julia_z;
@@ -377,7 +388,7 @@ bool MapgenFractal::getFractalAtPoint(s16 x, s16 y, s16 z)
                oy = (float)y / scale.Y - offset.Y;
                oz = (float)z / scale.Z - offset.Z;
                ow = slice_w;
-       } else {  // Mandelbrot sets, formula = 1, 3, 5, 7
+       } else {  // Mandelbrot set
                cx = (float)x / scale.X - offset.X;
                cy = (float)y / scale.Y - offset.Y;
                cz = (float)z / scale.Z - offset.Z;
@@ -388,32 +399,87 @@ bool MapgenFractal::getFractalAtPoint(s16 x, s16 y, s16 z)
                ow = 0.0f;
        }
 
+       float nx = 0.0f;
+       float ny = 0.0f;
+       float nz = 0.0f;
+       float nw = 0.0f;
+
        for (u16 iter = 0; iter < iterations; iter++) {
-               float nx = 0.0f;
-               float ny = 0.0f;
-               float nz = 0.0f;
-               float nw = 0.0f;
 
-               if (formula == 1 || formula == 2) {  // 4D "Roundy" Mandelbrot/Julia Set
+               if (formula == 1) {  // 4D "Roundy"
                        nx = ox * ox - oy * oy - oz * oz - ow * ow + cx;
                        ny = 2.0f * (ox * oy + oz * ow) + cy;
                        nz = 2.0f * (ox * oz + oy * ow) + cz;
                        nw = 2.0f * (ox * ow + oy * oz) + cw;
-               } else if (formula == 3 || formula == 4) {  // 4D "Squarry" Mandelbrot/Julia Set
+               } else if (formula == 2) {  // 4D "Squarry"
                        nx = ox * ox - oy * oy - oz * oz - ow * ow + cx;
                        ny = 2.0f * (ox * oy + oz * ow) + cy;
                        nz = 2.0f * (ox * oz + oy * ow) + cz;
                        nw = 2.0f * (ox * ow - oy * oz) + cw;
-               } else if (formula == 5 || formula == 6) {  // 4D "Mandy Cousin" Mandelbrot/Julia Set
+               } else if (formula == 3) {  // 4D "Mandy Cousin"
                        nx = ox * ox - oy * oy - oz * oz + ow * ow + cx;
                        ny = 2.0f * (ox * oy + oz * ow) + cy;
                        nz = 2.0f * (ox * oz + oy * ow) + cz;
                        nw = 2.0f * (ox * ow + oy * oz) + cw;
-               } else if (formula == 7 || formula == 8) {  // 4D "Variation" Mandelbrot/Julia Set
+               } else if (formula == 4) {  // 4D "Variation"
                        nx = ox * ox - oy * oy - oz * oz - ow * ow + cx;
                        ny = 2.0f * (ox * oy + oz * ow) + cy;
                        nz = 2.0f * (ox * oz - oy * ow) + cz;
                        nw = 2.0f * (ox * ow + oy * oz) + cw;
+               } else if (formula == 5) {  // 3D "Mandelbrot/Mandelbar"
+                       nx = ox * ox - oy * oy - oz * oz + cx;
+                       ny = 2.0f * ox * oy + cy;
+                       nz = -2.0f * ox * oz + cz;
+               } else if (formula == 6) {  // 3D "Christmas Tree"
+                       // Altering the formula here is necessary to avoid division by zero
+                       if (fabs(oz) < 0.000000001f) {
+                               nx = ox * ox - oy * oy - oz * oz + cx;
+                               ny = 2.0f * oy * ox + cy;
+                               nz = 4.0f * oz * ox + cz;
+                       } else {
+                               float a = (2.0f * ox) / (sqrt(oy * oy + oz * oz));
+                               nx = ox * ox - oy * oy - oz * oz + cx;
+                               ny = a * (oy * oy - oz * oz) + cy;
+                               nz = a * 2.0f * oy * oz + cz;
+                       }
+               } else if (formula == 7) {  // 3D "Mandelbulb"
+                       if (fabs(oy) < 0.000000001f) {
+                               nx = ox * ox - oz * oz + cx;
+                               ny = cy;
+                               nz = -2.0f * oz * sqrt(ox * ox) + cz;
+                       } else {
+                               float a = 1.0f - (oz * oz) / (ox * ox + oy * oy);
+                               nx = (ox * ox - oy * oy) * a + cx;
+                               ny = 2.0f * ox * oy * a + cy;
+                               nz = -2.0f * oz * sqrt(ox * ox + oy * oy) + cz;
+                       }
+               } else if (formula == 8) {  // 3D "Cosine Mandelbulb"
+                       if (fabs(oy) < 0.000000001f) {
+                               nx = 2.0f * ox * oz + cx;
+                               ny = 4.0f * oy * oz + cy;
+                               nz = oz * oz - ox * ox - oy * oy + cz;
+                       } else {
+                               float a = (2.0f * oz) / sqrt(ox * ox + oy * oy);
+                               nx = (ox * ox - oy * oy) * a + cx;
+                               ny = 2.0f * ox * oy * a + cy;
+                               nz = oz * oz - ox * ox - oy * oy + cz;
+                       }
+               } else if (formula == 9) {  // 4D "Mandelbulb"
+                       float rxy = sqrt(ox * ox + oy * oy);
+                       float rxyz = sqrt(ox * ox + oy * oy + oz * oz);
+                       if (fabs(ow) < 0.000000001f && fabs(oz) < 0.000000001f) {
+                               nx = (ox * ox - oy * oy) + cx;
+                               ny = 2.0f * ox * oy + cy;
+                               nz = -2.0f * rxy * oz + cz;
+                               nw = 2.0f * rxyz * ow + cw;
+                       } else {
+                               float a = 1.0f - (ow * ow) / (rxyz * rxyz);
+                               float b = a * (1.0f - (oz * oz) / (rxy * rxy));
+                               nx = (ox * ox - oy * oy) * b + cx;
+                               ny = 2.0f * ox * oy * b + cy;
+                               nz = -2.0f * rxy * oz * a + cz;
+                               nw = 2.0f * rxyz * ow + cw;
+                       }
                }
 
                if (nx * nx + ny * ny + nz * nz + nw * nw > 4.0f)
@@ -615,22 +681,45 @@ void MapgenFractal::dustTopNodes()
 
 void MapgenFractal::generateCaves(s16 max_stone_y)
 {
-       if (max_stone_y >= node_min.Y) {
-               u32 index = 0;
+       if (max_stone_y < node_min.Y)
+               return;
 
-               for (s16 z = node_min.Z; z <= node_max.Z; z++)
-               for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
-                       u32 vi = vm->m_area.index(node_min.X, y, z);
-                       for (s16 x = node_min.X; x <= node_max.X; x++, vi++, index++) {
-                               float d1 = contour(noise_cave1->result[index]);
-                               float d2 = contour(noise_cave2->result[index]);
-                               if (d1 * d2 > 0.3f) {
-                                       content_t c = vm->m_data[vi].getContent();
-                                       if (!ndef->get(c).is_ground_content || c == CONTENT_AIR)
-                                               continue;
-
-                                       vm->m_data[vi] = MapNode(CONTENT_AIR);
-                               }
+       noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+       noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+
+       v3s16 em = vm->m_area.getExtent();
+       u32 index2d = 0;
+
+       for (s16 z = node_min.Z; z <= node_max.Z; z++)
+       for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) {
+               bool column_is_open = false;  // Is column open to overground
+               u32 vi = vm->m_area.index(x, node_max.Y + 1, z);
+               u32 index3d = (z - node_min.Z) * zstride + (csize.Y + 1) * ystride +
+                       (x - node_min.X);
+               // Biome of column
+               Biome *biome = (Biome *)bmgr->getRaw(biomemap[index2d]);
+
+               for (s16 y = node_max.Y + 1; y >= node_min.Y - 1;
+                               y--, index3d -= 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;
+                       }
+                       // Ground
+                       float d1 = contour(noise_cave1->result[index3d]);
+                       float d2 = contour(noise_cave2->result[index3d]);
+                       if (d1 * d2 > 0.3f && ndef->get(c).is_ground_content) {
+                               // In tunnel and ground content, excavate
+                               vm->m_data[vi] = MapNode(CONTENT_AIR);
+                       } else if (column_is_open &&
+                                       (c == biome->c_filler || c == biome->c_stone)) {
+                               // Tunnel entrance floor
+                               vm->m_data[vi] = MapNode(biome->c_top);
+                               column_is_open = false;
+                       } else {
+                               column_is_open = false;
                        }
                }
        }