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, EmergeParams *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;
164 delete m_emerge; // our responsibility
168 MapgenV6Params::MapgenV6Params():
169 np_terrain_base (-4, 20.0, v3f(250.0, 250.0, 250.0), 82341, 5, 0.6, 2.0),
170 np_terrain_higher (20, 16.0, v3f(500.0, 500.0, 500.0), 85039, 5, 0.6, 2.0),
171 np_steepness (0.85, 0.5, v3f(125.0, 125.0, 125.0), -932, 5, 0.7, 2.0),
172 np_height_select (0, 1.0, v3f(250.0, 250.0, 250.0), 4213, 5, 0.69, 2.0),
173 np_mud (4, 2.0, v3f(200.0, 200.0, 200.0), 91013, 3, 0.55, 2.0),
174 np_beach (0, 1.0, v3f(250.0, 250.0, 250.0), 59420, 3, 0.50, 2.0),
175 np_biome (0, 1.0, v3f(500.0, 500.0, 500.0), 9130, 3, 0.50, 2.0),
176 np_cave (6, 6.0, v3f(250.0, 250.0, 250.0), 34329, 3, 0.50, 2.0),
177 np_humidity (0.5, 0.5, v3f(500.0, 500.0, 500.0), 72384, 3, 0.50, 2.0),
178 np_trees (0, 1.0, v3f(125.0, 125.0, 125.0), 2, 4, 0.66, 2.0),
179 np_apple_trees (0, 1.0, v3f(100.0, 100.0, 100.0), 342902, 3, 0.45, 2.0)
184 void MapgenV6Params::readParams(const Settings *settings)
186 settings->getFlagStrNoEx("mgv6_spflags", spflags, flagdesc_mapgen_v6);
187 settings->getFloatNoEx("mgv6_freq_desert", freq_desert);
188 settings->getFloatNoEx("mgv6_freq_beach", freq_beach);
189 settings->getS16NoEx("mgv6_dungeon_ymin", dungeon_ymin);
190 settings->getS16NoEx("mgv6_dungeon_ymax", dungeon_ymax);
192 settings->getNoiseParams("mgv6_np_terrain_base", np_terrain_base);
193 settings->getNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
194 settings->getNoiseParams("mgv6_np_steepness", np_steepness);
195 settings->getNoiseParams("mgv6_np_height_select", np_height_select);
196 settings->getNoiseParams("mgv6_np_mud", np_mud);
197 settings->getNoiseParams("mgv6_np_beach", np_beach);
198 settings->getNoiseParams("mgv6_np_biome", np_biome);
199 settings->getNoiseParams("mgv6_np_cave", np_cave);
200 settings->getNoiseParams("mgv6_np_humidity", np_humidity);
201 settings->getNoiseParams("mgv6_np_trees", np_trees);
202 settings->getNoiseParams("mgv6_np_apple_trees", np_apple_trees);
206 void MapgenV6Params::writeParams(Settings *settings) const
208 settings->setFlagStr("mgv6_spflags", spflags, flagdesc_mapgen_v6);
209 settings->setFloat("mgv6_freq_desert", freq_desert);
210 settings->setFloat("mgv6_freq_beach", freq_beach);
211 settings->setS16("mgv6_dungeon_ymin", dungeon_ymin);
212 settings->setS16("mgv6_dungeon_ymax", dungeon_ymax);
214 settings->setNoiseParams("mgv6_np_terrain_base", np_terrain_base);
215 settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
216 settings->setNoiseParams("mgv6_np_steepness", np_steepness);
217 settings->setNoiseParams("mgv6_np_height_select", np_height_select);
218 settings->setNoiseParams("mgv6_np_mud", np_mud);
219 settings->setNoiseParams("mgv6_np_beach", np_beach);
220 settings->setNoiseParams("mgv6_np_biome", np_biome);
221 settings->setNoiseParams("mgv6_np_cave", np_cave);
222 settings->setNoiseParams("mgv6_np_humidity", np_humidity);
223 settings->setNoiseParams("mgv6_np_trees", np_trees);
224 settings->setNoiseParams("mgv6_np_apple_trees", np_apple_trees);
228 void MapgenV6Params::setDefaultSettings(Settings *settings)
230 settings->setDefault("mgv6_spflags", flagdesc_mapgen_v6, MGV6_JUNGLES |
231 MGV6_SNOWBIOMES | MGV6_TREES | MGV6_BIOMEBLEND | MGV6_MUDFLOW);
235 //////////////////////// Some helper functions for the map generator
238 // Returns Y one under area minimum if not found
239 s16 MapgenV6::find_stone_level(v2s16 p2d)
241 const v3s16 &em = vm->m_area.getExtent();
242 s16 y_nodes_max = vm->m_area.MaxEdge.Y;
243 s16 y_nodes_min = vm->m_area.MinEdge.Y;
244 u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
247 for (y = y_nodes_max; y >= y_nodes_min; y--) {
248 content_t c = vm->m_data[i].getContent();
249 if (c != CONTENT_IGNORE && (c == c_stone || c == c_desert_stone))
252 VoxelArea::add_y(em, i, -1);
254 return (y >= y_nodes_min) ? y : y_nodes_min - 1;
258 // Required by mapgen.h
259 bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos)
261 /*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
262 seed, v2s16(blockpos.X, blockpos.Z));*/
263 // Nah, this is just a heuristic, just return something
264 s16 minimum_groundlevel = water_level;
266 if(blockpos.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
273 //////////////////////// Base terrain height functions
275 float MapgenV6::baseTerrainLevel(float terrain_base, float terrain_higher,
276 float steepness, float height_select)
278 float base = 1 + terrain_base;
279 float higher = 1 + terrain_higher;
281 // Limit higher ground level to at least base
285 // Steepness factor of cliffs
287 b = rangelim(b, 0.0, 1000.0);
288 b = 5 * b * b * b * b * b * b * b;
289 b = rangelim(b, 0.5, 1000.0);
291 // Values 1.5...100 give quite horrible looking slopes
292 if (b > 1.5 && b < 100.0)
293 b = (b < 10.0) ? 1.5 : 100.0;
295 float a_off = -0.20; // Offset to more low
296 float a = 0.5 + b * (a_off + height_select);
297 a = rangelim(a, 0.0, 1.0); // Limit
299 return base * (1.0 - a) + higher * a;
303 float MapgenV6::baseTerrainLevelFromNoise(v2s16 p)
305 if (spflags & MGV6_FLAT)
308 float terrain_base = NoisePerlin2D_PO(&noise_terrain_base->np,
309 p.X, 0.5, p.Y, 0.5, seed);
310 float terrain_higher = NoisePerlin2D_PO(&noise_terrain_higher->np,
311 p.X, 0.5, p.Y, 0.5, seed);
312 float steepness = NoisePerlin2D_PO(&noise_steepness->np,
313 p.X, 0.5, p.Y, 0.5, seed);
314 float height_select = NoisePerlin2D_PO(&noise_height_select->np,
315 p.X, 0.5, p.Y, 0.5, seed);
317 return baseTerrainLevel(terrain_base, terrain_higher,
318 steepness, height_select);
322 float MapgenV6::baseTerrainLevelFromMap(v2s16 p)
324 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
325 return baseTerrainLevelFromMap(index);
329 float MapgenV6::baseTerrainLevelFromMap(int index)
331 if (spflags & MGV6_FLAT)
334 float terrain_base = noise_terrain_base->result[index];
335 float terrain_higher = noise_terrain_higher->result[index];
336 float steepness = noise_steepness->result[index];
337 float height_select = noise_height_select->result[index];
339 return baseTerrainLevel(terrain_base, terrain_higher,
340 steepness, height_select);
344 int MapgenV6::getGroundLevelAtPoint(v2s16 p)
346 return baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
350 int MapgenV6::getSpawnLevelAtPoint(v2s16 p)
352 s16 level_at_point = baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
353 if (level_at_point <= water_level ||
354 level_at_point > water_level + 16)
355 return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
357 return level_at_point;
361 //////////////////////// Noise functions
364 BiomeV6Type MapgenV6::getBiome(v2s16 p)
366 int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
367 + (p.X - full_node_min.X);
368 return getBiome(index, p);
372 float MapgenV6::getHumidity(v2s16 p)
374 /*double noise = noise2d_perlin(
375 0.5+(float)p.X/500, 0.5+(float)p.Y/500,
376 seed+72384, 4, 0.66);
377 noise = (noise + 1.0)/2.0;*/
379 int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
380 + (p.X - full_node_min.X);
381 float noise = noise_humidity->result[index];
391 float MapgenV6::getTreeAmount(v2s16 p)
393 /*double noise = noise2d_perlin(
394 0.5+(float)p.X/125, 0.5+(float)p.Y/125,
397 float noise = NoisePerlin2D(np_trees, p.X, p.Y, seed);
398 float zeroval = -0.39;
402 return 0.04 * (noise - zeroval) / (1.0 - zeroval);
406 bool MapgenV6::getHaveAppleTree(v2s16 p)
408 /*is_apple_tree = noise2d_perlin(
409 0.5+(float)p.X/100, 0.5+(float)p.Z/100,
410 data->seed+342902, 3, 0.45) > 0.2;*/
412 float noise = NoisePerlin2D(np_apple_trees, p.X, p.Y, seed);
418 float MapgenV6::getMudAmount(int index)
420 if (spflags & MGV6_FLAT)
421 return MGV6_AVERAGE_MUD_AMOUNT;
423 /*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin(
424 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
425 seed+91013, 3, 0.55));*/
427 return noise_mud->result[index];
431 bool MapgenV6::getHaveBeach(int index)
433 // Determine whether to have sand here
434 /*double sandnoise = noise2d_perlin(
435 0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
436 seed+59420, 3, 0.50);*/
438 float sandnoise = noise_beach->result[index];
439 return (sandnoise > freq_beach);
443 BiomeV6Type MapgenV6::getBiome(int index, v2s16 p)
445 // Just do something very simple as for now
446 /*double d = noise2d_perlin(
447 0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
448 seed+9130, 3, 0.50);*/
450 float d = noise_biome->result[index];
451 float h = noise_humidity->result[index];
453 if (spflags & MGV6_SNOWBIOMES) {
454 float blend = (spflags & MGV6_BIOMEBLEND) ? noise2d(p.X, p.Y, seed) / 40 : 0;
456 if (d > MGV6_FREQ_HOT + blend) {
457 if (h > MGV6_FREQ_JUNGLE + blend)
463 if (d < MGV6_FREQ_SNOW + blend) {
464 if (h > MGV6_FREQ_TAIGA + blend)
476 if ((spflags & MGV6_BIOMEBLEND) && (d > freq_desert - 0.10) &&
477 ((noise2d(p.X, p.Y, seed) + 1.0) > (freq_desert - d) * 20.0))
480 if ((spflags & MGV6_JUNGLES) && h > 0.75)
488 u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
490 s32 x = p.X, y = p.Y, z = p.Z;
491 return (u32)(seed % 0x100000000ULL) + z * 38134234 + y * 42123 + x * 23;
495 //////////////////////// Map generator
497 void MapgenV6::makeChunk(BlockMakeData *data)
500 assert(data->vmanip);
501 assert(data->nodedef);
503 this->generating = true;
504 this->vm = data->vmanip;
505 this->ndef = data->nodedef;
507 // Hack: use minimum block coords for old code that assumes a single block
508 v3s16 blockpos_min = data->blockpos_min;
509 v3s16 blockpos_max = data->blockpos_max;
511 // Area of central chunk
512 node_min = blockpos_min * MAP_BLOCKSIZE;
513 node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
515 // Full allocated area
516 full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
517 full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
519 central_area_size = node_max - node_min + v3s16(1, 1, 1);
520 assert(central_area_size.X == central_area_size.Z);
522 // Create a block-specific seed
523 blockseed = get_blockseed(data->seed, full_node_min);
528 // Maximum height of the stone surface and obstacles.
529 // This is used to guide the cave generation
530 s16 stone_surface_max_y;
532 // Generate general ground level to full area
533 stone_surface_max_y = generateGround();
535 // Create initial heightmap to limit caves
536 updateHeightmap(node_min, node_max);
538 const s16 max_spread_amount = MAP_BLOCKSIZE;
539 // Limit dirt flow area by 1 because mud is flowed into neighbors
540 s16 mudflow_minpos = -max_spread_amount + 1;
541 s16 mudflow_maxpos = central_area_size.X + max_spread_amount - 2;
543 // Loop this part, it will make stuff look older and newer nicely
544 const u32 age_loops = 2;
545 for (u32 i_age = 0; i_age < age_loops; i_age++) { // Aging loop
546 // Make caves (this code is relatively horrible)
547 if (flags & MG_CAVES)
548 generateCaves(stone_surface_max_y);
550 // Add mud to the central chunk
553 // Flow mud away from steep edges
554 if (spflags & MGV6_MUDFLOW)
555 flowMud(mudflow_minpos, mudflow_maxpos);
559 // Update heightmap after mudflow
560 updateHeightmap(node_min, node_max);
563 if ((flags & MG_DUNGEONS) && stone_surface_max_y >= node_min.Y &&
564 full_node_min.Y >= dungeon_ymin && full_node_max.Y <= dungeon_ymax) {
565 u16 num_dungeons = std::fmax(std::floor(
566 NoisePerlin3D(&np_dungeons, node_min.X, node_min.Y, node_min.Z, seed)), 0.0f);
568 if (num_dungeons >= 1) {
569 PseudoRandom ps(blockseed + 4713);
574 dp.num_dungeons = num_dungeons;
575 dp.only_in_ground = true;
576 dp.corridor_len_min = 1;
577 dp.corridor_len_max = 13;
578 dp.num_rooms = ps.range(2, 16);
579 dp.large_room_chance = (ps.range(1, 4) == 1) ? 1 : 0;
582 = NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);
584 if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
585 dp.c_wall = c_desert_stone;
586 dp.c_alt_wall = CONTENT_IGNORE;
587 dp.c_stair = c_stair_desert_stone;
589 dp.diagonal_dirs = true;
590 dp.holesize = v3s16(2, 3, 2);
591 dp.room_size_min = v3s16(6, 9, 6);
592 dp.room_size_max = v3s16(10, 11, 10);
593 dp.room_size_large_min = v3s16(10, 13, 10);
594 dp.room_size_large_max = v3s16(18, 21, 18);
595 dp.notifytype = GENNOTIFY_TEMPLE;
597 dp.c_wall = c_cobble;
598 dp.c_alt_wall = c_mossycobble;
599 dp.c_stair = c_stair_cobble;
601 dp.diagonal_dirs = false;
602 dp.holesize = v3s16(1, 2, 1);
603 dp.room_size_min = v3s16(4, 4, 4);
604 dp.room_size_max = v3s16(8, 6, 8);
605 dp.room_size_large_min = v3s16(8, 8, 8);
606 dp.room_size_large_max = v3s16(16, 16, 16);
607 dp.notifytype = GENNOTIFY_DUNGEON;
610 DungeonGen dgen(ndef, &gennotify, &dp);
611 dgen.generate(vm, blockseed, full_node_min, full_node_max);
615 // Add top and bottom side of water to transforming_liquid queue
616 updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
621 // Generate some trees, and add grass, if a jungle
622 if (spflags & MGV6_TREES)
623 placeTreesAndJungleGrass();
625 // Generate the registered decorations
626 if (flags & MG_DECORATIONS)
627 m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
629 // Generate the registered ores
631 m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
633 // Calculate lighting
634 if (flags & MG_LIGHT)
635 calcLighting(node_min - v3s16(1, 1, 1) * MAP_BLOCKSIZE,
636 node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE,
637 full_node_min, full_node_max);
639 this->generating = false;
643 void MapgenV6::calculateNoise()
647 int fx = full_node_min.X;
648 int fz = full_node_min.Z;
650 if (!(spflags & MGV6_FLAT)) {
651 noise_terrain_base->perlinMap2D_PO(x, 0.5, z, 0.5);
652 noise_terrain_higher->perlinMap2D_PO(x, 0.5, z, 0.5);
653 noise_steepness->perlinMap2D_PO(x, 0.5, z, 0.5);
654 noise_height_select->perlinMap2D_PO(x, 0.5, z, 0.5);
655 noise_mud->perlinMap2D_PO(x, 0.5, z, 0.5);
658 noise_beach->perlinMap2D_PO(x, 0.2, z, 0.7);
660 noise_biome->perlinMap2D_PO(fx, 0.6, fz, 0.2);
661 noise_humidity->perlinMap2D_PO(fx, 0.0, fz, 0.0);
662 // Humidity map does not need range limiting 0 to 1,
663 // only humidity at point does
667 int MapgenV6::generateGround()
669 //TimeTaker timer1("Generating ground level");
670 MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
671 MapNode n_stone(c_stone), n_desert_stone(c_desert_stone);
672 MapNode n_ice(c_ice);
673 int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
676 for (s16 z = node_min.Z; z <= node_max.Z; z++)
677 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
679 s16 surface_y = (s16)baseTerrainLevelFromMap(index);
682 if (surface_y > stone_surface_max_y)
683 stone_surface_max_y = surface_y;
685 BiomeV6Type bt = getBiome(v2s16(x, z));
687 // Fill ground with stone
688 const v3s16 &em = vm->m_area.getExtent();
689 u32 i = vm->m_area.index(x, node_min.Y, z);
690 for (s16 y = node_min.Y; y <= node_max.Y; y++) {
691 if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
692 if (y <= surface_y) {
693 vm->m_data[i] = (y >= MGV6_DESERT_STONE_BASE
694 && bt == BT_DESERT) ?
695 n_desert_stone : n_stone;
696 } else if (y <= water_level) {
697 vm->m_data[i] = (y >= MGV6_ICE_BASE
698 && bt == BT_TUNDRA) ?
699 n_ice : n_water_source;
701 vm->m_data[i] = n_air;
704 VoxelArea::add_y(em, i, 1);
708 return stone_surface_max_y;
712 void MapgenV6::addMud()
715 //TimeTaker timer1("add mud");
716 MapNode n_dirt(c_dirt), n_gravel(c_gravel);
717 MapNode n_sand(c_sand), n_desert_sand(c_desert_sand);
721 for (s16 z = node_min.Z; z <= node_max.Z; z++)
722 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
723 // Randomize mud amount
724 s16 mud_add_amount = getMudAmount(index) / 2.0 + 0.5;
727 s16 surface_y = find_stone_level(v2s16(x, z)); /////////////////optimize this!
729 // Handle area not found
730 if (surface_y == vm->m_area.MinEdge.Y - 1)
733 BiomeV6Type bt = getBiome(v2s16(x, z));
734 addnode = (bt == BT_DESERT) ? n_desert_sand : n_dirt;
736 if (bt == BT_DESERT && surface_y + mud_add_amount <= water_level + 1) {
738 } else if (mud_add_amount <= 0) {
739 mud_add_amount = 1 - mud_add_amount;
741 } else if (bt != BT_DESERT && getHaveBeach(index) &&
742 surface_y + mud_add_amount <= water_level + 2) {
746 if ((bt == BT_DESERT || bt == BT_TUNDRA) && surface_y > 20)
747 mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20) / 5);
749 /* If topmost node is grass, change it to mud. It might be if it was
750 // flown to there from a neighboring chunk and then converted.
751 u32 i = vm->m_area.index(x, surface_y, z);
752 if (vm->m_data[i].getContent() == c_dirt_with_grass)
753 vm->m_data[i] = n_dirt;*/
757 const v3s16 &em = vm->m_area.getExtent();
758 s16 y_start = surface_y + 1;
759 u32 i = vm->m_area.index(x, y_start, z);
760 for (s16 y = y_start; y <= node_max.Y; y++) {
761 if (mudcount >= mud_add_amount)
764 vm->m_data[i] = addnode;
767 VoxelArea::add_y(em, i, 1);
773 void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
775 const v3s16 &em = vm->m_area.getExtent();
776 static const v3s16 dirs4[4] = {
777 v3s16(0, 0, 1), // Back
778 v3s16(1, 0, 0), // Right
779 v3s16(0, 0, -1), // Front
780 v3s16(-1, 0, 0), // Left
784 for (s16 k = 0; k < 2; k++) {
785 for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++)
786 for (s16 x = mudflow_minpos; x <= mudflow_maxpos; x++) {
787 // Node column position
789 // Invert coordinates on second iteration to process columns in
790 // opposite order, to avoid a directional bias.
792 p2d = v2s16(node_max.X, node_max.Z) - v2s16(x, z);
794 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x, z);
798 while (y >= node_min.Y) {
800 u32 i = vm->m_area.index(p2d.X, y, p2d.Y);
801 MapNode *n = nullptr;
803 // Find next mud node in mapchunk column
804 for (; y >= node_min.Y; y--) {
806 if (n->getContent() == c_dirt ||
807 n->getContent() == c_dirt_with_grass ||
808 n->getContent() == c_gravel)
811 VoxelArea::add_y(em, i, -1);
814 // No mud found in mapchunk column, process the next column
817 if (n->getContent() == c_dirt || n->getContent() == c_dirt_with_grass) {
818 // Convert dirt_with_grass to dirt
819 n->setContent(c_dirt);
820 // Don't flow mud if the stuff under it is not mud,
821 // to leave at least 1 node of mud.
823 VoxelArea::add_y(em, i2, -1);
824 MapNode *n2 = &vm->m_data[i2];
825 if (n2->getContent() != c_dirt &&
826 n2->getContent() != c_dirt_with_grass)
827 // Find next mud node in column
831 // Check if node above is walkable. If so, cancel
832 // flowing as if node above keeps it in place.
834 VoxelArea::add_y(em, i3, 1);
835 MapNode *n3 = &vm->m_data[i3];
836 if (ndef->get(*n3).walkable)
837 // Find next mud node in column
840 // Drop mud on one side
841 for (const v3s16 &dirp : dirs4) {
844 VoxelArea::add_p(em, i2, dirp);
845 // Check that side is air
846 MapNode *n2 = &vm->m_data[i2];
847 if (ndef->get(*n2).walkable)
850 // Check that under side is air
851 VoxelArea::add_y(em, i2, -1);
852 n2 = &vm->m_data[i2];
853 if (ndef->get(*n2).walkable)
856 // Loop further down until not air
857 s16 y2 = y - 1; // y of i2
858 bool dropped_to_unknown = false;
861 VoxelArea::add_y(em, i2, -1);
862 n2 = &vm->m_data[i2];
863 // If out of area or in ungenerated world
864 if (y2 < full_node_min.Y || n2->getContent() == CONTENT_IGNORE) {
865 dropped_to_unknown = true;
868 } while (!ndef->get(*n2).walkable);
870 if (!dropped_to_unknown) {
871 // Move up one so that we're in air
872 VoxelArea::add_y(em, i2, 1);
873 // Move mud to new place, and if outside mapchunk remove
874 // any decorations above removed or placed mud.
875 moveMud(i, i2, i3, p2d, em);
877 // Done, find next mud node in column
887 void MapgenV6::moveMud(u32 remove_index, u32 place_index,
888 u32 above_remove_index, v2s16 pos, v3s16 em)
890 MapNode n_air(CONTENT_AIR);
891 // Copy mud from old place to new place
892 vm->m_data[place_index] = vm->m_data[remove_index];
893 // Set old place to be air
894 vm->m_data[remove_index] = n_air;
895 // Outside the mapchunk decorations may need to be removed if above removed
896 // mud or if half-buried in placed mud. Placed mud is to the side of pos so
897 // use 'pos.X >= node_max.X' etc.
898 if (pos.X >= node_max.X || pos.X <= node_min.X ||
899 pos.Y >= node_max.Z || pos.Y <= node_min.Z) {
900 // 'above remove' node is above removed mud. If it is not air, water or
901 // 'ignore' it is a decoration that needs removing. Also search upwards
902 // to remove a possible stacked decoration.
903 // Check for 'ignore' because stacked decorations can penetrate into
904 // 'ignore' nodes above the mapchunk.
905 while (vm->m_area.contains(above_remove_index) &&
906 vm->m_data[above_remove_index].getContent() != CONTENT_AIR &&
907 vm->m_data[above_remove_index].getContent() != c_water_source &&
908 vm->m_data[above_remove_index].getContent() != CONTENT_IGNORE) {
909 vm->m_data[above_remove_index] = n_air;
910 VoxelArea::add_y(em, above_remove_index, 1);
912 // Mud placed may have partially-buried a stacked decoration, search
914 VoxelArea::add_y(em, place_index, 1);
915 while (vm->m_area.contains(place_index) &&
916 vm->m_data[place_index].getContent() != CONTENT_AIR &&
917 vm->m_data[place_index].getContent() != c_water_source &&
918 vm->m_data[place_index].getContent() != CONTENT_IGNORE) {
919 vm->m_data[place_index] = n_air;
920 VoxelArea::add_y(em, place_index, 1);
926 void MapgenV6::placeTreesAndJungleGrass()
928 //TimeTaker t("placeTrees");
929 if (node_max.Y < water_level)
932 PseudoRandom grassrandom(blockseed + 53);
933 content_t c_junglegrass = ndef->getId("mapgen_junglegrass");
934 // if we don't have junglegrass, don't place cignore... that's bad
935 if (c_junglegrass == CONTENT_IGNORE)
936 c_junglegrass = CONTENT_AIR;
937 MapNode n_junglegrass(c_junglegrass);
938 const v3s16 &em = vm->m_area.getExtent();
940 // Divide area into parts
942 s16 sidelen = central_area_size.X / div;
943 double area = sidelen * sidelen;
945 // N.B. We must add jungle grass first, since tree leaves will
946 // obstruct the ground, giving us a false ground level
947 for (s16 z0 = 0; z0 < div; z0++)
948 for (s16 x0 = 0; x0 < div; x0++) {
949 // Center position of part of division
951 node_min.X + sidelen / 2 + sidelen * x0,
952 node_min.Z + sidelen / 2 + sidelen * z0
954 // Minimum edge of part of division
956 node_min.X + sidelen * x0,
957 node_min.Z + sidelen * z0
959 // Maximum edge of part of division
961 node_min.X + sidelen + sidelen * x0 - 1,
962 node_min.Z + sidelen + sidelen * z0 - 1
965 // Get biome at center position of part of division
966 BiomeV6Type bt = getBiome(p2d_center);
970 if (bt == BT_JUNGLE || bt == BT_TAIGA || bt == BT_NORMAL) {
971 tree_count = area * getTreeAmount(p2d_center);
979 if (bt == BT_JUNGLE) {
980 float humidity = getHumidity(p2d_center);
981 u32 grass_count = 5 * humidity * tree_count;
982 for (u32 i = 0; i < grass_count; i++) {
983 s16 x = grassrandom.range(p2d_min.X, p2d_max.X);
984 s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y);
985 int mapindex = central_area_size.X * (z - node_min.Z)
987 s16 y = heightmap[mapindex];
991 u32 vi = vm->m_area.index(x, y, z);
992 // place on dirt_with_grass, since we know it is exposed to sunlight
993 if (vm->m_data[vi].getContent() == c_dirt_with_grass) {
994 VoxelArea::add_y(em, vi, 1);
995 vm->m_data[vi] = n_junglegrass;
1000 // Put trees in random places on part of division
1001 for (u32 i = 0; i < tree_count; i++) {
1002 s16 x = myrand_range(p2d_min.X, p2d_max.X);
1003 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
1004 int mapindex = central_area_size.X * (z - node_min.Z)
1006 s16 y = heightmap[mapindex];
1007 // Don't make a tree under water level
1008 // Don't make a tree so high that it doesn't fit
1009 if (y < water_level || y > node_max.Y - 6)
1013 // Trees grow only on mud and grass
1015 u32 i = vm->m_area.index(p);
1016 content_t c = vm->m_data[i].getContent();
1018 c != c_dirt_with_grass &&
1019 c != c_dirt_with_snow)
1025 if (bt == BT_JUNGLE) {
1026 treegen::make_jungletree(*vm, p, ndef, myrand());
1027 } else if (bt == BT_TAIGA) {
1028 treegen::make_pine_tree(*vm, p - v3s16(0, 1, 0), ndef, myrand());
1029 } else if (bt == BT_NORMAL) {
1030 bool is_apple_tree = (myrand_range(0, 3) == 0) &&
1031 getHaveAppleTree(v2s16(x, z));
1032 treegen::make_tree(*vm, p, is_apple_tree, ndef, myrand());
1036 //printf("placeTreesAndJungleGrass: %dms\n", t.stop());
1040 void MapgenV6::growGrass() // Add surface nodes
1042 MapNode n_dirt_with_grass(c_dirt_with_grass);
1043 MapNode n_dirt_with_snow(c_dirt_with_snow);
1044 MapNode n_snowblock(c_snowblock);
1045 const v3s16 &em = vm->m_area.getExtent();
1048 for (s16 z = full_node_min.Z; z <= full_node_max.Z; z++)
1049 for (s16 x = full_node_min.X; x <= full_node_max.X; x++, index++) {
1050 // Find the lowest surface to which enough light ends up to make
1051 // grass grow. Basically just wait until not air and not leaves.
1054 u32 i = vm->m_area.index(x, node_max.Y, z);
1056 // Go to ground level
1057 for (y = node_max.Y; y >= full_node_min.Y; y--) {
1058 MapNode &n = vm->m_data[i];
1059 if (ndef->get(n).param_type != CPT_LIGHT ||
1060 ndef->get(n).liquid_type != LIQUID_NONE ||
1061 n.getContent() == c_ice)
1063 VoxelArea::add_y(em, i, -1);
1065 surface_y = (y >= full_node_min.Y) ? y : full_node_min.Y;
1068 BiomeV6Type bt = getBiome(index, v2s16(x, z));
1069 u32 i = vm->m_area.index(x, surface_y, z);
1070 content_t c = vm->m_data[i].getContent();
1071 if (surface_y >= water_level - 20) {
1072 if (bt == BT_TAIGA && c == c_dirt) {
1073 vm->m_data[i] = n_dirt_with_snow;
1074 } else if (bt == BT_TUNDRA) {
1076 vm->m_data[i] = n_snowblock;
1077 VoxelArea::add_y(em, i, -1);
1078 vm->m_data[i] = n_dirt_with_snow;
1079 } else if (c == c_stone && surface_y < node_max.Y) {
1080 VoxelArea::add_y(em, i, 1);
1081 vm->m_data[i] = n_snowblock;
1083 } else if (c == c_dirt) {
1084 vm->m_data[i] = n_dirt_with_grass;
1091 void MapgenV6::generateCaves(int max_stone_y)
1093 float cave_amount = NoisePerlin2D(np_cave, node_min.X, node_min.Y, seed);
1094 int volume_nodes = (node_max.X - node_min.X + 1) *
1095 (node_max.Y - node_min.Y + 1) * MAP_BLOCKSIZE;
1096 cave_amount = MYMAX(0.0, cave_amount);
1097 u32 caves_count = cave_amount * volume_nodes / 50000;
1098 u32 bruises_count = 1;
1099 PseudoRandom ps(blockseed + 21343);
1100 PseudoRandom ps2(blockseed + 1032);
1102 if (ps.range(1, 6) == 1)
1103 bruises_count = ps.range(0, ps.range(0, 2));
1105 if (getBiome(v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
1110 for (u32 i = 0; i < caves_count + bruises_count; i++) {
1111 CavesV6 cave(ndef, &gennotify, water_level, c_water_source, c_lava_source);
1113 bool large_cave = (i >= caves_count);
1114 cave.makeCave(vm, node_min, node_max, &ps, &ps2,
1115 large_cave, max_stone_y, heightmap);