]> git.lizzy.rs Git - minetest.git/blobdiff - src/mapgen_fractal.cpp
Remove ClientMap::m_camera_mutex
[minetest.git] / src / mapgen_fractal.cpp
index 144fa29b213a87e1245f90c19f0e5ccefbaa54c7..8a5c1e2bc8dde8b2d87f80d6c305880f15d6f9a7 100644 (file)
@@ -1,7 +1,7 @@
 /*
 Minetest
-Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Additional development and fractal code by paramat
+Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2010-2015 paramat, Matt Gregory
 
 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
@@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "content_sao.h"
 #include "nodedef.h"
 #include "voxelalgorithms.h"
+//#include "profiler.h" // For TimeTaker
 #include "settings.h" // For g_settings
 #include "emerge.h"
 #include "dungeongen.h"
@@ -40,7 +41,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 
 FlagDesc flagdesc_mapgen_fractal[] = {
-       {"julia", MGFRACTAL_JULIA},
        {NULL,    0}
 };
 
@@ -58,24 +58,35 @@ MapgenFractal::MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager *
        this->ystride = csize.X;
        this->zstride = csize.X * (csize.Y + 2);
 
-       this->biomemap        = new u8[csize.X * csize.Z];
-       this->heightmap       = new s16[csize.X * csize.Z];
-       this->heatmap         = NULL;
-       this->humidmap        = NULL;
+       this->biomemap  = new u8[csize.X * csize.Z];
+       this->heightmap = new s16[csize.X * csize.Z];
+       this->heatmap   = NULL;
+       this->humidmap  = NULL;
 
        MapgenFractalParams *sp = (MapgenFractalParams *)params->sparams;
        this->spflags = sp->spflags;
+
+       this->fractal    = sp->fractal;
        this->iterations = sp->iterations;
-       this->scale_x = sp->scale_x;
-       this->scale_y = sp->scale_y;
-       this->scale_z = sp->scale_z;
-       this->offset_x = sp->offset_x;
-       this->offset_y = sp->offset_y;
-       this->offset_z = sp->offset_z;
+       this->scale      = sp->scale;
+       this->offset     = sp->offset;
+       this->slice_w    = sp->slice_w;
+
+       this->julia_x = sp->julia_x;
+       this->julia_y = sp->julia_y;
+       this->julia_z = sp->julia_z;
+       this->julia_w = sp->julia_w;
+
+       this->formula = fractal / 2 + fractal % 2;
+       this->julia   = fractal % 2 == 0;
 
-       //// 3d terrain noise
-       noise_cave1    = new Noise(&sp->np_cave1,    seed, csize.X, csize.Y + 2, csize.Z);
-       noise_cave2    = new Noise(&sp->np_cave2,    seed, csize.X, csize.Y + 2, csize.Z);
+       //// 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);
+
+       //// 3D terrain noise
+       noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 2, csize.Z);
+       noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 2, csize.Z);
 
        //// Biome noise
        noise_heat           = new Noise(&params->np_biome_heat,           seed, csize.X, csize.Z);
@@ -114,6 +125,8 @@ MapgenFractal::MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager *
 
 MapgenFractal::~MapgenFractal()
 {
+       delete noise_seabed;
+       delete noise_filler_depth;
        delete noise_cave1;
        delete noise_cave2;
 
@@ -131,21 +144,21 @@ MapgenFractalParams::MapgenFractalParams()
 {
        spflags = 0;
 
-       iterations = 9;
-       scale_x = 1024;
-       scale_y = 256;
-       scale_z = 1024;
-       offset_x = -1.75;
-       offset_y = 0;
-       offset_z = 0;
-       slice_w = 0.5;
+       fractal = 1;
+       iterations = 11;
+       scale = v3f(4096.0, 1024.0, 4096.0);
+       offset = v3f(1.79, 0.0, 0.0);
+       slice_w = 0.0;
+
        julia_x = 0.33;
        julia_y = 0.33;
        julia_z = 0.33;
        julia_w = 0.33;
 
-       np_cave1 = NoiseParams(0, 12, v3f(128, 128, 128), 52534, 4, 0.5, 2.0);
-       np_cave2 = NoiseParams(0, 12, v3f(128, 128, 128), 10325, 4, 0.5, 2.0);
+       np_seabed       = NoiseParams(-14, 9,   v3f(600, 600, 600), 41900, 5, 0.6, 2.0);
+       np_filler_depth = NoiseParams(0,   1.2, v3f(150, 150, 150), 261,   3, 0.7, 2.0);
+       np_cave1        = NoiseParams(0,   12,  v3f(96,  96,  96),  52534, 4, 0.5, 2.0);
+       np_cave2        = NoiseParams(0,   12,  v3f(96,  96,  96),  10325, 4, 0.5, 2.0);
 }
 
 
@@ -153,19 +166,19 @@ void MapgenFractalParams::readParams(const Settings *settings)
 {
        settings->getFlagStrNoEx("mgfractal_spflags", spflags, flagdesc_mapgen_fractal);
 
+       settings->getU16NoEx("mgfractal_fractal", fractal);
        settings->getU16NoEx("mgfractal_iterations", iterations);
-       settings->getFloatNoEx("mgfractal_scale_x", scale_x);
-       settings->getFloatNoEx("mgfractal_scale_y", scale_y);
-       settings->getFloatNoEx("mgfractal_scale_z", scale_z);
-       settings->getFloatNoEx("mgfractal_offset_x", offset_x);
-       settings->getFloatNoEx("mgfractal_offset_y", offset_y);
-       settings->getFloatNoEx("mgfractal_offset_z", offset_z);
+       settings->getV3FNoEx("mgfractal_scale", scale);
+       settings->getV3FNoEx("mgfractal_offset", offset);
        settings->getFloatNoEx("mgfractal_slice_w", slice_w);
+
        settings->getFloatNoEx("mgfractal_julia_x", julia_x);
        settings->getFloatNoEx("mgfractal_julia_y", julia_y);
        settings->getFloatNoEx("mgfractal_julia_z", julia_z);
        settings->getFloatNoEx("mgfractal_julia_w", julia_w);
 
+       settings->getNoiseParams("mgfractal_np_seabed", np_seabed);
+       settings->getNoiseParams("mgfractal_np_filler_depth", np_filler_depth);
        settings->getNoiseParams("mgfractal_np_cave1", np_cave1);
        settings->getNoiseParams("mgfractal_np_cave2", np_cave2);
 }
@@ -175,19 +188,19 @@ void MapgenFractalParams::writeParams(Settings *settings) const
 {
        settings->setFlagStr("mgfractal_spflags", spflags, flagdesc_mapgen_fractal, U32_MAX);
 
+       settings->setU16("mgfractal_fractal", fractal);
        settings->setU16("mgfractal_iterations", iterations);
-       settings->setFloat("mgfractal_scale_x", scale_x);
-       settings->setFloat("mgfractal_scale_y", scale_y);
-       settings->setFloat("mgfractal_scale_z", scale_z);
-       settings->setFloat("mgfractal_offset_x", offset_x);
-       settings->setFloat("mgfractal_offset_y", offset_y);
-       settings->setFloat("mgfractal_offset_z", offset_z);
+       settings->setV3F("mgfractal_scale", scale);
+       settings->setV3F("mgfractal_offset", offset);
        settings->setFloat("mgfractal_slice_w", slice_w);
+
        settings->setFloat("mgfractal_julia_x", julia_x);
        settings->setFloat("mgfractal_julia_y", julia_y);
        settings->setFloat("mgfractal_julia_z", julia_z);
        settings->setFloat("mgfractal_julia_w", julia_w);
 
+       settings->setNoiseParams("mgfractal_np_seabed", np_seabed);
+       settings->setNoiseParams("mgfractal_np_filler_depth", np_filler_depth);
        settings->setNoiseParams("mgfractal_np_cave1", np_cave1);
        settings->setNoiseParams("mgfractal_np_cave2", np_cave2);
 }
@@ -196,17 +209,28 @@ void MapgenFractalParams::writeParams(Settings *settings) const
 /////////////////////////////////////////////////////////////////
 
 
-int MapgenFractal::getGroundLevelAtPoint(v2s16 p)
+int MapgenFractal::getSpawnLevelAtPoint(v2s16 p)
 {
-       s16 search_top = water_level + 128;
-       s16 search_base = water_level;
-       s16 level = -MAX_MAP_GENERATION_LIMIT;
-       for (s16 y = search_top; y >= search_base; y--) {
-               if (getTerrainAtPoint(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 level;
+       return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
 }
 
 
@@ -299,7 +323,8 @@ void MapgenFractal::makeChunk(BlockMakeData *data)
        }
 
        // Generate the registered decorations
-       m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
+       if (flags & MG_DECORATIONS)
+               m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
 
        // Generate the registered ores
        m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
@@ -325,15 +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;
 
-       if (flags & MG_CAVES) {
-               noise_cave1->perlinMap3D(x, y, z);
-               noise_cave2->perlinMap3D(x, y, z);
-       }
+       noise_seabed->perlinMap2D(x, 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);
@@ -350,41 +375,116 @@ void MapgenFractal::calculateNoise()
 }
 
 
-bool MapgenFractal::getTerrainAtPoint(s16 x, s16 y, s16 z)
+bool MapgenFractal::getFractalAtPoint(s16 x, s16 y, s16 z)
 {
        float cx, cy, cz, cw, ox, oy, oz, ow;
 
-       if (spflags & MGFRACTAL_JULIA) {  // Julia set
+       if (julia) {  // Julia set
                cx = julia_x;
                cy = julia_y;
                cz = julia_z;
                cw = julia_w;
-               ox = (float)x / scale_x + offset_x;
-               oy = (float)y / scale_y + offset_y;
-               oz = (float)z / scale_z + offset_z;
+               ox = (float)x / scale.X - offset.X;
+               oy = (float)y / scale.Y - offset.Y;
+               oz = (float)z / scale.Z - offset.Z;
                ow = slice_w;
        } else {  // Mandelbrot set
-               cx = (float)x / scale_x + offset_x;
-               cy = (float)y / scale_y + offset_y;
-               cz = (float)z / scale_z + offset_z;
+               cx = (float)x / scale.X - offset.X;
+               cy = (float)y / scale.Y - offset.Y;
+               cz = (float)z / scale.Z - offset.Z;
                cw = slice_w;
-               ox = 0.0;
-               oy = 0.0;
-               oz = 0.0;
-               ow = 0.0;
+               ox = 0.0f;
+               oy = 0.0f;
+               oz = 0.0f;
+               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++) {
-               // 4D "Roundy" Mandelbrot set
-               float nx = ox * ox - oy * oy - oz * oz - ow * ow + cx;
-               float ny = 2.0 * (ox * oy + oz * ow) + cy;
-               float nz = 2.0 * (ox * oz + oy * ow) + cz;
-               float nw = 2.0 * (ox * ow + oy * oz) + cw;
 
-               if (nx * nx + ny * ny + nz * nz + nw * nw > 4.0) {
-                       return false;
+               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 == 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 == 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 == 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)
+                       return false;
+
                ox = nx;
                oy = ny;
                oz = nz;
@@ -402,23 +502,29 @@ s16 MapgenFractal::generateTerrain()
        MapNode n_water(c_water_source);
 
        s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
+       u32 index2d = 0;
 
-       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 i = vm->m_area.index(node_min.X, y, z);
-               for (s16 x = node_min.X; x <= node_max.X; x++, i++) {
-                       if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
-                               if (getTerrainAtPoint(x, y, z)) {
-                                       vm->m_data[i] = n_stone;
-                                       if (y > stone_surface_max_y)
-                                               stone_surface_max_y = y;
-                               } else if (y <= water_level) {
-                                       vm->m_data[i] = n_water;
-                               } else {
-                                       vm->m_data[i] = n_air;
+       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++, index2d++) {
+                               if (vm->m_data[vi].getContent() == CONTENT_IGNORE) {
+                                       s16 seabed_height = noise_seabed->result[index2d];
+
+                                       if (y <= seabed_height || getFractalAtPoint(x, y, z)) {
+                                               vm->m_data[vi] = n_stone;
+                                               if (y > stone_surface_max_y)
+                                                       stone_surface_max_y = y;
+                                       } else if (y <= water_level) {
+                                               vm->m_data[vi] = n_water;
+                                       } else {
+                                               vm->m_data[vi] = n_air;
+                                       }
                                }
                        }
+                       index2d -= ystride;
                }
+               index2d += ystride;
        }
 
        return stone_surface_max_y;
@@ -464,7 +570,8 @@ MgStoneType MapgenFractal::generateBiomes(float *heat_map, float *humidity_map)
                                        (c == c_water_source && (air_above || !biome))) {
                                biome = bmgr->getBiome(heat_map[index], humidity_map[index], y);
                                depth_top = biome->depth_top;
-                               base_filler = depth_top + biome->depth_filler;
+                               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.
@@ -574,24 +681,56 @@ 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 i = vm->m_area.index(node_min.X, y, z);
-                       for (s16 x = node_min.X; x <= node_max.X; x++, i++, index++) {
-                               float d1 = contour(noise_cave1->result[index]);
-                               float d2 = contour(noise_cave2->result[index]);
-                               if (d1 * d2 > 0.3) {
-                                       content_t c = vm->m_data[i].getContent();
-                                       if (!ndef->get(c).is_ground_content || c == CONTENT_AIR)
-                                               continue;
-
-                                       vm->m_data[i] = 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;
                        }
                }
        }
-}
 
+       if (node_max.Y > MGFRACTAL_LARGE_CAVE_DEPTH)
+               return;
+
+       PseudoRandom ps(blockseed + 21343);
+       u32 bruises_count = ps.range(0, 2);
+       for (u32 i = 0; i < bruises_count; i++) {
+               CaveV5 cave(this, &ps);
+               cave.makeCave(node_min, node_max, max_stone_y);
+       }
+}