3 Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2013-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
5 Copyright (C) 2014-2018 paramat
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 #include "voxelalgorithms.h"
32 //#include "profiler.h" // For TimeTaker
33 #include "settings.h" // For g_settings
35 #include "dungeongen.h"
39 #include "mg_decoration.h"
40 #include "mapgen_v6.h"
43 FlagDesc flagdesc_mapgen_v6[] = {
44 {"jungles", MGV6_JUNGLES},
45 {"biomeblend", MGV6_BIOMEBLEND},
46 {"mudflow", MGV6_MUDFLOW},
47 {"snowbiomes", MGV6_SNOWBIOMES},
49 {"trees", MGV6_TREES},
54 /////////////////////////////////////////////////////////////////////////////
57 MapgenV6::MapgenV6(MapgenV6Params *params, EmergeManager *emerge)
58 : Mapgen(MAPGEN_V6, params, emerge)
63 heightmap = new s16[csize.X * csize.Z];
65 spflags = params->spflags;
66 freq_desert = params->freq_desert;
67 freq_beach = params->freq_beach;
68 dungeon_ymin = params->dungeon_ymin;
69 dungeon_ymax = params->dungeon_ymax;
71 np_cave = ¶ms->np_cave;
72 np_humidity = ¶ms->np_humidity;
73 np_trees = ¶ms->np_trees;
74 np_apple_trees = ¶ms->np_apple_trees;
76 np_dungeons = NoiseParams(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0);
78 //// Create noise objects
79 noise_terrain_base = new Noise(¶ms->np_terrain_base, seed, csize.X, csize.Y);
80 noise_terrain_higher = new Noise(¶ms->np_terrain_higher, seed, csize.X, csize.Y);
81 noise_steepness = new Noise(¶ms->np_steepness, seed, csize.X, csize.Y);
82 noise_height_select = new Noise(¶ms->np_height_select, seed, csize.X, csize.Y);
83 noise_mud = new Noise(¶ms->np_mud, seed, csize.X, csize.Y);
84 noise_beach = new Noise(¶ms->np_beach, seed, csize.X, csize.Y);
85 noise_biome = new Noise(¶ms->np_biome, seed,
86 csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
87 noise_humidity = new Noise(¶ms->np_humidity, seed,
88 csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
90 //// Resolve nodes to be used
91 const NodeDefManager *ndef = emerge->ndef;
93 c_stone = ndef->getId("mapgen_stone");
94 c_dirt = ndef->getId("mapgen_dirt");
95 c_dirt_with_grass = ndef->getId("mapgen_dirt_with_grass");
96 c_sand = ndef->getId("mapgen_sand");
97 c_water_source = ndef->getId("mapgen_water_source");
98 c_lava_source = ndef->getId("mapgen_lava_source");
99 c_gravel = ndef->getId("mapgen_gravel");
100 c_desert_stone = ndef->getId("mapgen_desert_stone");
101 c_desert_sand = ndef->getId("mapgen_desert_sand");
102 c_dirt_with_snow = ndef->getId("mapgen_dirt_with_snow");
103 c_snow = ndef->getId("mapgen_snow");
104 c_snowblock = ndef->getId("mapgen_snowblock");
105 c_ice = ndef->getId("mapgen_ice");
107 if (c_gravel == CONTENT_IGNORE)
109 if (c_desert_stone == CONTENT_IGNORE)
110 c_desert_stone = c_stone;
111 if (c_desert_sand == CONTENT_IGNORE)
112 c_desert_sand = c_sand;
113 if (c_dirt_with_snow == CONTENT_IGNORE)
114 c_dirt_with_snow = c_dirt_with_grass;
115 if (c_snow == CONTENT_IGNORE)
116 c_snow = CONTENT_AIR;
117 if (c_snowblock == CONTENT_IGNORE)
118 c_snowblock = c_dirt_with_grass;
119 if (c_ice == CONTENT_IGNORE)
120 c_ice = c_water_source;
122 c_cobble = ndef->getId("mapgen_cobble");
123 c_mossycobble = ndef->getId("mapgen_mossycobble");
124 c_stair_cobble = ndef->getId("mapgen_stair_cobble");
125 c_stair_desert_stone = ndef->getId("mapgen_stair_desert_stone");
127 if (c_mossycobble == CONTENT_IGNORE)
128 c_mossycobble = c_cobble;
129 if (c_stair_cobble == CONTENT_IGNORE)
130 c_stair_cobble = c_cobble;
131 if (c_stair_desert_stone == CONTENT_IGNORE)
132 c_stair_desert_stone = c_desert_stone;
134 if (c_stone == CONTENT_IGNORE)
135 errorstream << "Mapgen v6: Mapgen alias 'mapgen_stone' is invalid!" << std::endl;
136 if (c_dirt == CONTENT_IGNORE)
137 errorstream << "Mapgen v6: Mapgen alias 'mapgen_dirt' is invalid!" << std::endl;
138 if (c_dirt_with_grass == CONTENT_IGNORE)
139 errorstream << "Mapgen v6: Mapgen alias 'mapgen_dirt_with_grass' is invalid!" << std::endl;
140 if (c_sand == CONTENT_IGNORE)
141 errorstream << "Mapgen v6: Mapgen alias 'mapgen_sand' is invalid!" << std::endl;
142 if (c_water_source == CONTENT_IGNORE)
143 errorstream << "Mapgen v6: Mapgen alias 'mapgen_water_source' is invalid!" << std::endl;
144 if (c_lava_source == CONTENT_IGNORE)
145 errorstream << "Mapgen v6: Mapgen alias 'mapgen_lava_source' is invalid!" << std::endl;
146 if (c_cobble == CONTENT_IGNORE)
147 errorstream << "Mapgen v6: Mapgen alias 'mapgen_cobble' is invalid!" << std::endl;
151 MapgenV6::~MapgenV6()
153 delete noise_terrain_base;
154 delete noise_terrain_higher;
155 delete noise_steepness;
156 delete noise_height_select;
160 delete noise_humidity;
166 MapgenV6Params::MapgenV6Params():
167 np_terrain_base (-4, 20.0, v3f(250.0, 250.0, 250.0), 82341, 5, 0.6, 2.0),
168 np_terrain_higher (20, 16.0, v3f(500.0, 500.0, 500.0), 85039, 5, 0.6, 2.0),
169 np_steepness (0.85, 0.5, v3f(125.0, 125.0, 125.0), -932, 5, 0.7, 2.0),
170 np_height_select (0, 1.0, v3f(250.0, 250.0, 250.0), 4213, 5, 0.69, 2.0),
171 np_mud (4, 2.0, v3f(200.0, 200.0, 200.0), 91013, 3, 0.55, 2.0),
172 np_beach (0, 1.0, v3f(250.0, 250.0, 250.0), 59420, 3, 0.50, 2.0),
173 np_biome (0, 1.0, v3f(500.0, 500.0, 500.0), 9130, 3, 0.50, 2.0),
174 np_cave (6, 6.0, v3f(250.0, 250.0, 250.0), 34329, 3, 0.50, 2.0),
175 np_humidity (0.5, 0.5, v3f(500.0, 500.0, 500.0), 72384, 3, 0.50, 2.0),
176 np_trees (0, 1.0, v3f(125.0, 125.0, 125.0), 2, 4, 0.66, 2.0),
177 np_apple_trees (0, 1.0, v3f(100.0, 100.0, 100.0), 342902, 3, 0.45, 2.0)
182 void MapgenV6Params::readParams(const Settings *settings)
184 settings->getFlagStrNoEx("mgv6_spflags", spflags, flagdesc_mapgen_v6);
185 settings->getFloatNoEx("mgv6_freq_desert", freq_desert);
186 settings->getFloatNoEx("mgv6_freq_beach", freq_beach);
187 settings->getS16NoEx("mgv6_dungeon_ymin", dungeon_ymin);
188 settings->getS16NoEx("mgv6_dungeon_ymax", dungeon_ymax);
190 settings->getNoiseParams("mgv6_np_terrain_base", np_terrain_base);
191 settings->getNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
192 settings->getNoiseParams("mgv6_np_steepness", np_steepness);
193 settings->getNoiseParams("mgv6_np_height_select", np_height_select);
194 settings->getNoiseParams("mgv6_np_mud", np_mud);
195 settings->getNoiseParams("mgv6_np_beach", np_beach);
196 settings->getNoiseParams("mgv6_np_biome", np_biome);
197 settings->getNoiseParams("mgv6_np_cave", np_cave);
198 settings->getNoiseParams("mgv6_np_humidity", np_humidity);
199 settings->getNoiseParams("mgv6_np_trees", np_trees);
200 settings->getNoiseParams("mgv6_np_apple_trees", np_apple_trees);
204 void MapgenV6Params::writeParams(Settings *settings) const
206 settings->setFlagStr("mgv6_spflags", spflags, flagdesc_mapgen_v6);
207 settings->setFloat("mgv6_freq_desert", freq_desert);
208 settings->setFloat("mgv6_freq_beach", freq_beach);
209 settings->setS16("mgv6_dungeon_ymin", dungeon_ymin);
210 settings->setS16("mgv6_dungeon_ymax", dungeon_ymax);
212 settings->setNoiseParams("mgv6_np_terrain_base", np_terrain_base);
213 settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
214 settings->setNoiseParams("mgv6_np_steepness", np_steepness);
215 settings->setNoiseParams("mgv6_np_height_select", np_height_select);
216 settings->setNoiseParams("mgv6_np_mud", np_mud);
217 settings->setNoiseParams("mgv6_np_beach", np_beach);
218 settings->setNoiseParams("mgv6_np_biome", np_biome);
219 settings->setNoiseParams("mgv6_np_cave", np_cave);
220 settings->setNoiseParams("mgv6_np_humidity", np_humidity);
221 settings->setNoiseParams("mgv6_np_trees", np_trees);
222 settings->setNoiseParams("mgv6_np_apple_trees", np_apple_trees);
226 void MapgenV6Params::setDefaultSettings(Settings *settings)
228 settings->setDefault("mgv6_spflags", flagdesc_mapgen_v6, MGV6_JUNGLES |
229 MGV6_SNOWBIOMES | MGV6_TREES | MGV6_BIOMEBLEND | MGV6_MUDFLOW);
233 //////////////////////// Some helper functions for the map generator
236 // Returns Y one under area minimum if not found
237 s16 MapgenV6::find_stone_level(v2s16 p2d)
239 const v3s16 &em = vm->m_area.getExtent();
240 s16 y_nodes_max = vm->m_area.MaxEdge.Y;
241 s16 y_nodes_min = vm->m_area.MinEdge.Y;
242 u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
245 for (y = y_nodes_max; y >= y_nodes_min; y--) {
246 content_t c = vm->m_data[i].getContent();
247 if (c != CONTENT_IGNORE && (c == c_stone || c == c_desert_stone))
250 VoxelArea::add_y(em, i, -1);
252 return (y >= y_nodes_min) ? y : y_nodes_min - 1;
256 // Required by mapgen.h
257 bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos)
259 /*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
260 seed, v2s16(blockpos.X, blockpos.Z));*/
261 // Nah, this is just a heuristic, just return something
262 s16 minimum_groundlevel = water_level;
264 if(blockpos.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
271 //////////////////////// Base terrain height functions
273 float MapgenV6::baseTerrainLevel(float terrain_base, float terrain_higher,
274 float steepness, float height_select)
276 float base = 1 + terrain_base;
277 float higher = 1 + terrain_higher;
279 // Limit higher ground level to at least base
283 // Steepness factor of cliffs
285 b = rangelim(b, 0.0, 1000.0);
286 b = 5 * b * b * b * b * b * b * b;
287 b = rangelim(b, 0.5, 1000.0);
289 // Values 1.5...100 give quite horrible looking slopes
290 if (b > 1.5 && b < 100.0)
291 b = (b < 10.0) ? 1.5 : 100.0;
293 float a_off = -0.20; // Offset to more low
294 float a = 0.5 + b * (a_off + height_select);
295 a = rangelim(a, 0.0, 1.0); // Limit
297 return base * (1.0 - a) + higher * a;
301 float MapgenV6::baseTerrainLevelFromNoise(v2s16 p)
303 if (spflags & MGV6_FLAT)
306 float terrain_base = NoisePerlin2D_PO(&noise_terrain_base->np,
307 p.X, 0.5, p.Y, 0.5, seed);
308 float terrain_higher = NoisePerlin2D_PO(&noise_terrain_higher->np,
309 p.X, 0.5, p.Y, 0.5, seed);
310 float steepness = NoisePerlin2D_PO(&noise_steepness->np,
311 p.X, 0.5, p.Y, 0.5, seed);
312 float height_select = NoisePerlin2D_PO(&noise_height_select->np,
313 p.X, 0.5, p.Y, 0.5, seed);
315 return baseTerrainLevel(terrain_base, terrain_higher,
316 steepness, height_select);
320 float MapgenV6::baseTerrainLevelFromMap(v2s16 p)
322 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
323 return baseTerrainLevelFromMap(index);
327 float MapgenV6::baseTerrainLevelFromMap(int index)
329 if (spflags & MGV6_FLAT)
332 float terrain_base = noise_terrain_base->result[index];
333 float terrain_higher = noise_terrain_higher->result[index];
334 float steepness = noise_steepness->result[index];
335 float height_select = noise_height_select->result[index];
337 return baseTerrainLevel(terrain_base, terrain_higher,
338 steepness, height_select);
342 s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
344 return baseTerrainLevelFromNoise(p2d) + MGV6_AVERAGE_MUD_AMOUNT;
348 int MapgenV6::getGroundLevelAtPoint(v2s16 p)
350 return baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
354 int MapgenV6::getSpawnLevelAtPoint(v2s16 p)
356 s16 level_at_point = baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
357 if (level_at_point <= water_level ||
358 level_at_point > water_level + 16)
359 return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
361 return level_at_point;
365 //////////////////////// Noise functions
367 float MapgenV6::getMudAmount(v2s16 p)
369 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
370 return getMudAmount(index);
374 bool MapgenV6::getHaveBeach(v2s16 p)
376 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
377 return getHaveBeach(index);
381 BiomeV6Type MapgenV6::getBiome(v2s16 p)
383 int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
384 + (p.X - full_node_min.X);
385 return getBiome(index, p);
389 float MapgenV6::getHumidity(v2s16 p)
391 /*double noise = noise2d_perlin(
392 0.5+(float)p.X/500, 0.5+(float)p.Y/500,
393 seed+72384, 4, 0.66);
394 noise = (noise + 1.0)/2.0;*/
396 int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
397 + (p.X - full_node_min.X);
398 float noise = noise_humidity->result[index];
408 float MapgenV6::getTreeAmount(v2s16 p)
410 /*double noise = noise2d_perlin(
411 0.5+(float)p.X/125, 0.5+(float)p.Y/125,
414 float noise = NoisePerlin2D(np_trees, p.X, p.Y, seed);
415 float zeroval = -0.39;
419 return 0.04 * (noise - zeroval) / (1.0 - zeroval);
423 bool MapgenV6::getHaveAppleTree(v2s16 p)
425 /*is_apple_tree = noise2d_perlin(
426 0.5+(float)p.X/100, 0.5+(float)p.Z/100,
427 data->seed+342902, 3, 0.45) > 0.2;*/
429 float noise = NoisePerlin2D(np_apple_trees, p.X, p.Y, seed);
435 float MapgenV6::getMudAmount(int index)
437 if (spflags & MGV6_FLAT)
438 return MGV6_AVERAGE_MUD_AMOUNT;
440 /*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin(
441 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
442 seed+91013, 3, 0.55));*/
444 return noise_mud->result[index];
448 bool MapgenV6::getHaveBeach(int index)
450 // Determine whether to have sand here
451 /*double sandnoise = noise2d_perlin(
452 0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
453 seed+59420, 3, 0.50);*/
455 float sandnoise = noise_beach->result[index];
456 return (sandnoise > freq_beach);
460 BiomeV6Type MapgenV6::getBiome(int index, v2s16 p)
462 // Just do something very simple as for now
463 /*double d = noise2d_perlin(
464 0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
465 seed+9130, 3, 0.50);*/
467 float d = noise_biome->result[index];
468 float h = noise_humidity->result[index];
470 if (spflags & MGV6_SNOWBIOMES) {
471 float blend = (spflags & MGV6_BIOMEBLEND) ? noise2d(p.X, p.Y, seed) / 40 : 0;
473 if (d > MGV6_FREQ_HOT + blend) {
474 if (h > MGV6_FREQ_JUNGLE + blend)
480 if (d < MGV6_FREQ_SNOW + blend) {
481 if (h > MGV6_FREQ_TAIGA + blend)
493 if ((spflags & MGV6_BIOMEBLEND) && (d > freq_desert - 0.10) &&
494 ((noise2d(p.X, p.Y, seed) + 1.0) > (freq_desert - d) * 20.0))
497 if ((spflags & MGV6_JUNGLES) && h > 0.75)
505 u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
507 s32 x = p.X, y = p.Y, z = p.Z;
508 return (u32)(seed % 0x100000000ULL) + z * 38134234 + y * 42123 + x * 23;
512 //////////////////////// Map generator
514 void MapgenV6::makeChunk(BlockMakeData *data)
517 assert(data->vmanip);
518 assert(data->nodedef);
519 assert(data->blockpos_requested.X >= data->blockpos_min.X &&
520 data->blockpos_requested.Y >= data->blockpos_min.Y &&
521 data->blockpos_requested.Z >= data->blockpos_min.Z);
522 assert(data->blockpos_requested.X <= data->blockpos_max.X &&
523 data->blockpos_requested.Y <= data->blockpos_max.Y &&
524 data->blockpos_requested.Z <= data->blockpos_max.Z);
526 this->generating = true;
527 this->vm = data->vmanip;
528 this->ndef = data->nodedef;
530 // Hack: use minimum block coords for old code that assumes a single block
531 v3s16 blockpos_min = data->blockpos_min;
532 v3s16 blockpos_max = data->blockpos_max;
534 // Area of central chunk
535 node_min = blockpos_min * MAP_BLOCKSIZE;
536 node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
538 // Full allocated area
539 full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
540 full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
542 central_area_size = node_max - node_min + v3s16(1, 1, 1);
543 assert(central_area_size.X == central_area_size.Z);
545 // Create a block-specific seed
546 blockseed = get_blockseed(data->seed, full_node_min);
551 // Maximum height of the stone surface and obstacles.
552 // This is used to guide the cave generation
553 s16 stone_surface_max_y;
555 // Generate general ground level to full area
556 stone_surface_max_y = generateGround();
558 // Create initial heightmap to limit caves
559 updateHeightmap(node_min, node_max);
561 const s16 max_spread_amount = MAP_BLOCKSIZE;
562 // Limit dirt flow area by 1 because mud is flowed into neighbors
563 s16 mudflow_minpos = -max_spread_amount + 1;
564 s16 mudflow_maxpos = central_area_size.X + max_spread_amount - 2;
566 // Loop this part, it will make stuff look older and newer nicely
567 const u32 age_loops = 2;
568 for (u32 i_age = 0; i_age < age_loops; i_age++) { // Aging loop
569 // Make caves (this code is relatively horrible)
570 if (flags & MG_CAVES)
571 generateCaves(stone_surface_max_y);
573 // Add mud to the central chunk
576 // Flow mud away from steep edges
577 if (spflags & MGV6_MUDFLOW)
578 flowMud(mudflow_minpos, mudflow_maxpos);
582 // Update heightmap after mudflow
583 updateHeightmap(node_min, node_max);
586 if ((flags & MG_DUNGEONS) && stone_surface_max_y >= node_min.Y &&
587 full_node_min.Y >= dungeon_ymin && full_node_max.Y <= dungeon_ymax) {
588 u16 num_dungeons = std::fmax(std::floor(
589 NoisePerlin3D(&np_dungeons, node_min.X, node_min.Y, node_min.Z, seed)), 0.0f);
591 if (num_dungeons >= 1) {
592 PseudoRandom ps(blockseed + 4713);
597 dp.num_dungeons = num_dungeons;
598 dp.only_in_ground = true;
599 dp.corridor_len_min = 1;
600 dp.corridor_len_max = 13;
601 dp.num_rooms = ps.range(2, 16);
602 dp.large_room_chance = (ps.range(1, 4) == 1) ? 1 : 0;
605 = NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);
607 if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
608 dp.c_wall = c_desert_stone;
609 dp.c_alt_wall = CONTENT_IGNORE;
610 dp.c_stair = c_stair_desert_stone;
612 dp.diagonal_dirs = true;
613 dp.holesize = v3s16(2, 3, 2);
614 dp.room_size_min = v3s16(6, 9, 6);
615 dp.room_size_max = v3s16(10, 11, 10);
616 dp.room_size_large_min = v3s16(10, 13, 10);
617 dp.room_size_large_max = v3s16(18, 21, 18);
618 dp.notifytype = GENNOTIFY_TEMPLE;
620 dp.c_wall = c_cobble;
621 dp.c_alt_wall = c_mossycobble;
622 dp.c_stair = c_stair_cobble;
624 dp.diagonal_dirs = false;
625 dp.holesize = v3s16(1, 2, 1);
626 dp.room_size_min = v3s16(4, 4, 4);
627 dp.room_size_max = v3s16(8, 6, 8);
628 dp.room_size_large_min = v3s16(8, 8, 8);
629 dp.room_size_large_max = v3s16(16, 16, 16);
630 dp.notifytype = GENNOTIFY_DUNGEON;
633 DungeonGen dgen(ndef, &gennotify, &dp);
634 dgen.generate(vm, blockseed, full_node_min, full_node_max);
638 // Add top and bottom side of water to transforming_liquid queue
639 updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
644 // Generate some trees, and add grass, if a jungle
645 if (spflags & MGV6_TREES)
646 placeTreesAndJungleGrass();
648 // Generate the registered decorations
649 if (flags & MG_DECORATIONS)
650 m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
652 // Generate the registered ores
653 m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
655 // Calculate lighting
656 if (flags & MG_LIGHT)
657 calcLighting(node_min - v3s16(1, 1, 1) * MAP_BLOCKSIZE,
658 node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE,
659 full_node_min, full_node_max);
661 this->generating = false;
665 void MapgenV6::calculateNoise()
669 int fx = full_node_min.X;
670 int fz = full_node_min.Z;
672 if (!(spflags & MGV6_FLAT)) {
673 noise_terrain_base->perlinMap2D_PO(x, 0.5, z, 0.5);
674 noise_terrain_higher->perlinMap2D_PO(x, 0.5, z, 0.5);
675 noise_steepness->perlinMap2D_PO(x, 0.5, z, 0.5);
676 noise_height_select->perlinMap2D_PO(x, 0.5, z, 0.5);
677 noise_mud->perlinMap2D_PO(x, 0.5, z, 0.5);
680 noise_beach->perlinMap2D_PO(x, 0.2, z, 0.7);
682 noise_biome->perlinMap2D_PO(fx, 0.6, fz, 0.2);
683 noise_humidity->perlinMap2D_PO(fx, 0.0, fz, 0.0);
684 // Humidity map does not need range limiting 0 to 1,
685 // only humidity at point does
689 int MapgenV6::generateGround()
691 //TimeTaker timer1("Generating ground level");
692 MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
693 MapNode n_stone(c_stone), n_desert_stone(c_desert_stone);
694 MapNode n_ice(c_ice);
695 int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
698 for (s16 z = node_min.Z; z <= node_max.Z; z++)
699 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
701 s16 surface_y = (s16)baseTerrainLevelFromMap(index);
704 if (surface_y > stone_surface_max_y)
705 stone_surface_max_y = surface_y;
707 BiomeV6Type bt = getBiome(v2s16(x, z));
709 // Fill ground with stone
710 const v3s16 &em = vm->m_area.getExtent();
711 u32 i = vm->m_area.index(x, node_min.Y, z);
712 for (s16 y = node_min.Y; y <= node_max.Y; y++) {
713 if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
714 if (y <= surface_y) {
715 vm->m_data[i] = (y >= MGV6_DESERT_STONE_BASE
716 && bt == BT_DESERT) ?
717 n_desert_stone : n_stone;
718 } else if (y <= water_level) {
719 vm->m_data[i] = (y >= MGV6_ICE_BASE
720 && bt == BT_TUNDRA) ?
721 n_ice : n_water_source;
723 vm->m_data[i] = n_air;
726 VoxelArea::add_y(em, i, 1);
730 return stone_surface_max_y;
734 void MapgenV6::addMud()
737 //TimeTaker timer1("add mud");
738 MapNode n_dirt(c_dirt), n_gravel(c_gravel);
739 MapNode n_sand(c_sand), n_desert_sand(c_desert_sand);
743 for (s16 z = node_min.Z; z <= node_max.Z; z++)
744 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
745 // Randomize mud amount
746 s16 mud_add_amount = getMudAmount(index) / 2.0 + 0.5;
749 s16 surface_y = find_stone_level(v2s16(x, z)); /////////////////optimize this!
751 // Handle area not found
752 if (surface_y == vm->m_area.MinEdge.Y - 1)
755 BiomeV6Type bt = getBiome(v2s16(x, z));
756 addnode = (bt == BT_DESERT) ? n_desert_sand : n_dirt;
758 if (bt == BT_DESERT && surface_y + mud_add_amount <= water_level + 1) {
760 } else if (mud_add_amount <= 0) {
761 mud_add_amount = 1 - mud_add_amount;
763 } else if (bt != BT_DESERT && getHaveBeach(index) &&
764 surface_y + mud_add_amount <= water_level + 2) {
768 if ((bt == BT_DESERT || bt == BT_TUNDRA) && surface_y > 20)
769 mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20) / 5);
771 /* If topmost node is grass, change it to mud. It might be if it was
772 // flown to there from a neighboring chunk and then converted.
773 u32 i = vm->m_area.index(x, surface_y, z);
774 if (vm->m_data[i].getContent() == c_dirt_with_grass)
775 vm->m_data[i] = n_dirt;*/
779 const v3s16 &em = vm->m_area.getExtent();
780 s16 y_start = surface_y + 1;
781 u32 i = vm->m_area.index(x, y_start, z);
782 for (s16 y = y_start; y <= node_max.Y; y++) {
783 if (mudcount >= mud_add_amount)
786 vm->m_data[i] = addnode;
789 VoxelArea::add_y(em, i, 1);
795 void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
797 const v3s16 &em = vm->m_area.getExtent();
798 static const v3s16 dirs4[4] = {
799 v3s16(0, 0, 1), // Back
800 v3s16(1, 0, 0), // Right
801 v3s16(0, 0, -1), // Front
802 v3s16(-1, 0, 0), // Left
806 for (s16 k = 0; k < 2; k++) {
807 for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++)
808 for (s16 x = mudflow_minpos; x <= mudflow_maxpos; x++) {
809 // Node column position
811 // Invert coordinates on second iteration to process columns in
812 // opposite order, to avoid a directional bias.
814 p2d = v2s16(node_max.X, node_max.Z) - v2s16(x, z);
816 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x, z);
820 while (y >= node_min.Y) {
822 u32 i = vm->m_area.index(p2d.X, y, p2d.Y);
823 MapNode *n = nullptr;
825 // Find next mud node in mapchunk column
826 for (; y >= node_min.Y; y--) {
828 if (n->getContent() == c_dirt ||
829 n->getContent() == c_dirt_with_grass ||
830 n->getContent() == c_gravel)
833 VoxelArea::add_y(em, i, -1);
836 // No mud found in mapchunk column, process the next column
839 if (n->getContent() == c_dirt || n->getContent() == c_dirt_with_grass) {
840 // Convert dirt_with_grass to dirt
841 n->setContent(c_dirt);
842 // Don't flow mud if the stuff under it is not mud,
843 // to leave at least 1 node of mud.
845 VoxelArea::add_y(em, i2, -1);
846 MapNode *n2 = &vm->m_data[i2];
847 if (n2->getContent() != c_dirt &&
848 n2->getContent() != c_dirt_with_grass)
849 // Find next mud node in column
853 // Check if node above is walkable. If so, cancel
854 // flowing as if node above keeps it in place.
856 VoxelArea::add_y(em, i3, 1);
857 MapNode *n3 = &vm->m_data[i3];
858 if (ndef->get(*n3).walkable)
859 // Find next mud node in column
862 // Drop mud on one side
863 for (const v3s16 &dirp : dirs4) {
866 VoxelArea::add_p(em, i2, dirp);
867 // Check that side is air
868 MapNode *n2 = &vm->m_data[i2];
869 if (ndef->get(*n2).walkable)
872 // Check that under side is air
873 VoxelArea::add_y(em, i2, -1);
874 n2 = &vm->m_data[i2];
875 if (ndef->get(*n2).walkable)
878 // Loop further down until not air
879 s16 y2 = y - 1; // y of i2
880 bool dropped_to_unknown = false;
883 VoxelArea::add_y(em, i2, -1);
884 n2 = &vm->m_data[i2];
885 // If out of area or in ungenerated world
886 if (y2 < full_node_min.Y || n2->getContent() == CONTENT_IGNORE) {
887 dropped_to_unknown = true;
890 } while (!ndef->get(*n2).walkable);
892 if (!dropped_to_unknown) {
893 // Move up one so that we're in air
894 VoxelArea::add_y(em, i2, 1);
895 // Move mud to new place, and if outside mapchunk remove
896 // any decorations above removed or placed mud.
897 moveMud(i, i2, i3, p2d, em);
899 // Done, find next mud node in column
909 void MapgenV6::moveMud(u32 remove_index, u32 place_index,
910 u32 above_remove_index, v2s16 pos, v3s16 em)
912 MapNode n_air(CONTENT_AIR);
913 // Copy mud from old place to new place
914 vm->m_data[place_index] = vm->m_data[remove_index];
915 // Set old place to be air
916 vm->m_data[remove_index] = n_air;
917 // Outside the mapchunk decorations may need to be removed if above removed
918 // mud or if half-buried in placed mud. Placed mud is to the side of pos so
919 // use 'pos.X >= node_max.X' etc.
920 if (pos.X >= node_max.X || pos.X <= node_min.X ||
921 pos.Y >= node_max.Z || pos.Y <= node_min.Z) {
922 // 'above remove' node is above removed mud. If it is not air, water or
923 // 'ignore' it is a decoration that needs removing. Also search upwards
924 // to remove a possible stacked decoration.
925 // Check for 'ignore' because stacked decorations can penetrate into
926 // 'ignore' nodes above the mapchunk.
927 while (vm->m_area.contains(above_remove_index) &&
928 vm->m_data[above_remove_index].getContent() != CONTENT_AIR &&
929 vm->m_data[above_remove_index].getContent() != c_water_source &&
930 vm->m_data[above_remove_index].getContent() != CONTENT_IGNORE) {
931 vm->m_data[above_remove_index] = n_air;
932 VoxelArea::add_y(em, above_remove_index, 1);
934 // Mud placed may have partially-buried a stacked decoration, search
936 VoxelArea::add_y(em, place_index, 1);
937 while (vm->m_area.contains(place_index) &&
938 vm->m_data[place_index].getContent() != CONTENT_AIR &&
939 vm->m_data[place_index].getContent() != c_water_source &&
940 vm->m_data[place_index].getContent() != CONTENT_IGNORE) {
941 vm->m_data[place_index] = n_air;
942 VoxelArea::add_y(em, place_index, 1);
948 void MapgenV6::placeTreesAndJungleGrass()
950 //TimeTaker t("placeTrees");
951 if (node_max.Y < water_level)
954 PseudoRandom grassrandom(blockseed + 53);
955 content_t c_junglegrass = ndef->getId("mapgen_junglegrass");
956 // if we don't have junglegrass, don't place cignore... that's bad
957 if (c_junglegrass == CONTENT_IGNORE)
958 c_junglegrass = CONTENT_AIR;
959 MapNode n_junglegrass(c_junglegrass);
960 const v3s16 &em = vm->m_area.getExtent();
962 // Divide area into parts
964 s16 sidelen = central_area_size.X / div;
965 double area = sidelen * sidelen;
967 // N.B. We must add jungle grass first, since tree leaves will
968 // obstruct the ground, giving us a false ground level
969 for (s16 z0 = 0; z0 < div; z0++)
970 for (s16 x0 = 0; x0 < div; x0++) {
971 // Center position of part of division
973 node_min.X + sidelen / 2 + sidelen * x0,
974 node_min.Z + sidelen / 2 + sidelen * z0
976 // Minimum edge of part of division
978 node_min.X + sidelen * x0,
979 node_min.Z + sidelen * z0
981 // Maximum edge of part of division
983 node_min.X + sidelen + sidelen * x0 - 1,
984 node_min.Z + sidelen + sidelen * z0 - 1
987 // Get biome at center position of part of division
988 BiomeV6Type bt = getBiome(p2d_center);
992 if (bt == BT_JUNGLE || bt == BT_TAIGA || bt == BT_NORMAL) {
993 tree_count = area * getTreeAmount(p2d_center);
1001 if (bt == BT_JUNGLE) {
1002 float humidity = getHumidity(p2d_center);
1003 u32 grass_count = 5 * humidity * tree_count;
1004 for (u32 i = 0; i < grass_count; i++) {
1005 s16 x = grassrandom.range(p2d_min.X, p2d_max.X);
1006 s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y);
1007 int mapindex = central_area_size.X * (z - node_min.Z)
1009 s16 y = heightmap[mapindex];
1010 if (y < water_level)
1013 u32 vi = vm->m_area.index(x, y, z);
1014 // place on dirt_with_grass, since we know it is exposed to sunlight
1015 if (vm->m_data[vi].getContent() == c_dirt_with_grass) {
1016 VoxelArea::add_y(em, vi, 1);
1017 vm->m_data[vi] = n_junglegrass;
1022 // Put trees in random places on part of division
1023 for (u32 i = 0; i < tree_count; i++) {
1024 s16 x = myrand_range(p2d_min.X, p2d_max.X);
1025 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
1026 int mapindex = central_area_size.X * (z - node_min.Z)
1028 s16 y = heightmap[mapindex];
1029 // Don't make a tree under water level
1030 // Don't make a tree so high that it doesn't fit
1031 if (y < water_level || y > node_max.Y - 6)
1035 // Trees grow only on mud and grass
1037 u32 i = vm->m_area.index(p);
1038 content_t c = vm->m_data[i].getContent();
1040 c != c_dirt_with_grass &&
1041 c != c_dirt_with_snow)
1047 if (bt == BT_JUNGLE) {
1048 treegen::make_jungletree(*vm, p, ndef, myrand());
1049 } else if (bt == BT_TAIGA) {
1050 treegen::make_pine_tree(*vm, p - v3s16(0, 1, 0), ndef, myrand());
1051 } else if (bt == BT_NORMAL) {
1052 bool is_apple_tree = (myrand_range(0, 3) == 0) &&
1053 getHaveAppleTree(v2s16(x, z));
1054 treegen::make_tree(*vm, p, is_apple_tree, ndef, myrand());
1058 //printf("placeTreesAndJungleGrass: %dms\n", t.stop());
1062 void MapgenV6::growGrass() // Add surface nodes
1064 MapNode n_dirt_with_grass(c_dirt_with_grass);
1065 MapNode n_dirt_with_snow(c_dirt_with_snow);
1066 MapNode n_snowblock(c_snowblock);
1067 MapNode n_snow(c_snow);
1068 const v3s16 &em = vm->m_area.getExtent();
1071 for (s16 z = full_node_min.Z; z <= full_node_max.Z; z++)
1072 for (s16 x = full_node_min.X; x <= full_node_max.X; x++, index++) {
1073 // Find the lowest surface to which enough light ends up to make
1074 // grass grow. Basically just wait until not air and not leaves.
1077 u32 i = vm->m_area.index(x, node_max.Y, z);
1079 // Go to ground level
1080 for (y = node_max.Y; y >= full_node_min.Y; y--) {
1081 MapNode &n = vm->m_data[i];
1082 if (ndef->get(n).param_type != CPT_LIGHT ||
1083 ndef->get(n).liquid_type != LIQUID_NONE ||
1084 n.getContent() == c_ice)
1086 VoxelArea::add_y(em, i, -1);
1088 surface_y = (y >= full_node_min.Y) ? y : full_node_min.Y;
1091 BiomeV6Type bt = getBiome(index, v2s16(x, z));
1092 u32 i = vm->m_area.index(x, surface_y, z);
1093 content_t c = vm->m_data[i].getContent();
1094 if (surface_y >= water_level - 20) {
1095 if (bt == BT_TAIGA && c == c_dirt) {
1096 vm->m_data[i] = n_dirt_with_snow;
1097 } else if (bt == BT_TUNDRA) {
1099 vm->m_data[i] = n_snowblock;
1100 VoxelArea::add_y(em, i, -1);
1101 vm->m_data[i] = n_dirt_with_snow;
1102 } else if (c == c_stone && surface_y < node_max.Y) {
1103 VoxelArea::add_y(em, i, 1);
1104 vm->m_data[i] = n_snowblock;
1106 } else if (c == c_dirt) {
1107 vm->m_data[i] = n_dirt_with_grass;
1114 void MapgenV6::generateCaves(int max_stone_y)
1116 float cave_amount = NoisePerlin2D(np_cave, node_min.X, node_min.Y, seed);
1117 int volume_nodes = (node_max.X - node_min.X + 1) *
1118 (node_max.Y - node_min.Y + 1) * MAP_BLOCKSIZE;
1119 cave_amount = MYMAX(0.0, cave_amount);
1120 u32 caves_count = cave_amount * volume_nodes / 50000;
1121 u32 bruises_count = 1;
1122 PseudoRandom ps(blockseed + 21343);
1123 PseudoRandom ps2(blockseed + 1032);
1125 if (ps.range(1, 6) == 1)
1126 bruises_count = ps.range(0, ps.range(0, 2));
1128 if (getBiome(v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
1133 for (u32 i = 0; i < caves_count + bruises_count; i++) {
1134 CavesV6 cave(ndef, &gennotify, water_level, c_water_source, c_lava_source);
1136 bool large_cave = (i >= caves_count);
1137 cave.makeCave(vm, node_min, node_max, &ps, &ps2,
1138 large_cave, max_stone_y, heightmap);