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
363 float MapgenV6::getMudAmount(v2s16 p)
365 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
366 return getMudAmount(index);
370 bool MapgenV6::getHaveBeach(v2s16 p)
372 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
373 return getHaveBeach(index);
377 BiomeV6Type MapgenV6::getBiome(v2s16 p)
379 int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
380 + (p.X - full_node_min.X);
381 return getBiome(index, p);
385 float MapgenV6::getHumidity(v2s16 p)
387 /*double noise = noise2d_perlin(
388 0.5+(float)p.X/500, 0.5+(float)p.Y/500,
389 seed+72384, 4, 0.66);
390 noise = (noise + 1.0)/2.0;*/
392 int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
393 + (p.X - full_node_min.X);
394 float noise = noise_humidity->result[index];
404 float MapgenV6::getTreeAmount(v2s16 p)
406 /*double noise = noise2d_perlin(
407 0.5+(float)p.X/125, 0.5+(float)p.Y/125,
410 float noise = NoisePerlin2D(np_trees, p.X, p.Y, seed);
411 float zeroval = -0.39;
415 return 0.04 * (noise - zeroval) / (1.0 - zeroval);
419 bool MapgenV6::getHaveAppleTree(v2s16 p)
421 /*is_apple_tree = noise2d_perlin(
422 0.5+(float)p.X/100, 0.5+(float)p.Z/100,
423 data->seed+342902, 3, 0.45) > 0.2;*/
425 float noise = NoisePerlin2D(np_apple_trees, p.X, p.Y, seed);
431 float MapgenV6::getMudAmount(int index)
433 if (spflags & MGV6_FLAT)
434 return MGV6_AVERAGE_MUD_AMOUNT;
436 /*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin(
437 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
438 seed+91013, 3, 0.55));*/
440 return noise_mud->result[index];
444 bool MapgenV6::getHaveBeach(int index)
446 // Determine whether to have sand here
447 /*double sandnoise = noise2d_perlin(
448 0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
449 seed+59420, 3, 0.50);*/
451 float sandnoise = noise_beach->result[index];
452 return (sandnoise > freq_beach);
456 BiomeV6Type MapgenV6::getBiome(int index, v2s16 p)
458 // Just do something very simple as for now
459 /*double d = noise2d_perlin(
460 0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
461 seed+9130, 3, 0.50);*/
463 float d = noise_biome->result[index];
464 float h = noise_humidity->result[index];
466 if (spflags & MGV6_SNOWBIOMES) {
467 float blend = (spflags & MGV6_BIOMEBLEND) ? noise2d(p.X, p.Y, seed) / 40 : 0;
469 if (d > MGV6_FREQ_HOT + blend) {
470 if (h > MGV6_FREQ_JUNGLE + blend)
476 if (d < MGV6_FREQ_SNOW + blend) {
477 if (h > MGV6_FREQ_TAIGA + blend)
489 if ((spflags & MGV6_BIOMEBLEND) && (d > freq_desert - 0.10) &&
490 ((noise2d(p.X, p.Y, seed) + 1.0) > (freq_desert - d) * 20.0))
493 if ((spflags & MGV6_JUNGLES) && h > 0.75)
501 u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
503 s32 x = p.X, y = p.Y, z = p.Z;
504 return (u32)(seed % 0x100000000ULL) + z * 38134234 + y * 42123 + x * 23;
508 //////////////////////// Map generator
510 void MapgenV6::makeChunk(BlockMakeData *data)
513 assert(data->vmanip);
514 assert(data->nodedef);
515 assert(data->blockpos_requested.X >= data->blockpos_min.X &&
516 data->blockpos_requested.Y >= data->blockpos_min.Y &&
517 data->blockpos_requested.Z >= data->blockpos_min.Z);
518 assert(data->blockpos_requested.X <= data->blockpos_max.X &&
519 data->blockpos_requested.Y <= data->blockpos_max.Y &&
520 data->blockpos_requested.Z <= data->blockpos_max.Z);
522 this->generating = true;
523 this->vm = data->vmanip;
524 this->ndef = data->nodedef;
526 // Hack: use minimum block coords for old code that assumes a single block
527 v3s16 blockpos_min = data->blockpos_min;
528 v3s16 blockpos_max = data->blockpos_max;
530 // Area of central chunk
531 node_min = blockpos_min * MAP_BLOCKSIZE;
532 node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
534 // Full allocated area
535 full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
536 full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
538 central_area_size = node_max - node_min + v3s16(1, 1, 1);
539 assert(central_area_size.X == central_area_size.Z);
541 // Create a block-specific seed
542 blockseed = get_blockseed(data->seed, full_node_min);
547 // Maximum height of the stone surface and obstacles.
548 // This is used to guide the cave generation
549 s16 stone_surface_max_y;
551 // Generate general ground level to full area
552 stone_surface_max_y = generateGround();
554 // Create initial heightmap to limit caves
555 updateHeightmap(node_min, node_max);
557 const s16 max_spread_amount = MAP_BLOCKSIZE;
558 // Limit dirt flow area by 1 because mud is flowed into neighbors
559 s16 mudflow_minpos = -max_spread_amount + 1;
560 s16 mudflow_maxpos = central_area_size.X + max_spread_amount - 2;
562 // Loop this part, it will make stuff look older and newer nicely
563 const u32 age_loops = 2;
564 for (u32 i_age = 0; i_age < age_loops; i_age++) { // Aging loop
565 // Make caves (this code is relatively horrible)
566 if (flags & MG_CAVES)
567 generateCaves(stone_surface_max_y);
569 // Add mud to the central chunk
572 // Flow mud away from steep edges
573 if (spflags & MGV6_MUDFLOW)
574 flowMud(mudflow_minpos, mudflow_maxpos);
578 // Update heightmap after mudflow
579 updateHeightmap(node_min, node_max);
582 if ((flags & MG_DUNGEONS) && stone_surface_max_y >= node_min.Y &&
583 full_node_min.Y >= dungeon_ymin && full_node_max.Y <= dungeon_ymax) {
584 u16 num_dungeons = std::fmax(std::floor(
585 NoisePerlin3D(&np_dungeons, node_min.X, node_min.Y, node_min.Z, seed)), 0.0f);
587 if (num_dungeons >= 1) {
588 PseudoRandom ps(blockseed + 4713);
593 dp.num_dungeons = num_dungeons;
594 dp.only_in_ground = true;
595 dp.corridor_len_min = 1;
596 dp.corridor_len_max = 13;
597 dp.num_rooms = ps.range(2, 16);
598 dp.large_room_chance = (ps.range(1, 4) == 1) ? 1 : 0;
601 = NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);
603 if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
604 dp.c_wall = c_desert_stone;
605 dp.c_alt_wall = CONTENT_IGNORE;
606 dp.c_stair = c_stair_desert_stone;
608 dp.diagonal_dirs = true;
609 dp.holesize = v3s16(2, 3, 2);
610 dp.room_size_min = v3s16(6, 9, 6);
611 dp.room_size_max = v3s16(10, 11, 10);
612 dp.room_size_large_min = v3s16(10, 13, 10);
613 dp.room_size_large_max = v3s16(18, 21, 18);
614 dp.notifytype = GENNOTIFY_TEMPLE;
616 dp.c_wall = c_cobble;
617 dp.c_alt_wall = c_mossycobble;
618 dp.c_stair = c_stair_cobble;
620 dp.diagonal_dirs = false;
621 dp.holesize = v3s16(1, 2, 1);
622 dp.room_size_min = v3s16(4, 4, 4);
623 dp.room_size_max = v3s16(8, 6, 8);
624 dp.room_size_large_min = v3s16(8, 8, 8);
625 dp.room_size_large_max = v3s16(16, 16, 16);
626 dp.notifytype = GENNOTIFY_DUNGEON;
629 DungeonGen dgen(ndef, &gennotify, &dp);
630 dgen.generate(vm, blockseed, full_node_min, full_node_max);
634 // Add top and bottom side of water to transforming_liquid queue
635 updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
640 // Generate some trees, and add grass, if a jungle
641 if (spflags & MGV6_TREES)
642 placeTreesAndJungleGrass();
644 // Generate the registered decorations
645 if (flags & MG_DECORATIONS)
646 m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
648 // Generate the registered ores
650 m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
652 // Calculate lighting
653 if (flags & MG_LIGHT)
654 calcLighting(node_min - v3s16(1, 1, 1) * MAP_BLOCKSIZE,
655 node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE,
656 full_node_min, full_node_max);
658 this->generating = false;
662 void MapgenV6::calculateNoise()
666 int fx = full_node_min.X;
667 int fz = full_node_min.Z;
669 if (!(spflags & MGV6_FLAT)) {
670 noise_terrain_base->perlinMap2D_PO(x, 0.5, z, 0.5);
671 noise_terrain_higher->perlinMap2D_PO(x, 0.5, z, 0.5);
672 noise_steepness->perlinMap2D_PO(x, 0.5, z, 0.5);
673 noise_height_select->perlinMap2D_PO(x, 0.5, z, 0.5);
674 noise_mud->perlinMap2D_PO(x, 0.5, z, 0.5);
677 noise_beach->perlinMap2D_PO(x, 0.2, z, 0.7);
679 noise_biome->perlinMap2D_PO(fx, 0.6, fz, 0.2);
680 noise_humidity->perlinMap2D_PO(fx, 0.0, fz, 0.0);
681 // Humidity map does not need range limiting 0 to 1,
682 // only humidity at point does
686 int MapgenV6::generateGround()
688 //TimeTaker timer1("Generating ground level");
689 MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
690 MapNode n_stone(c_stone), n_desert_stone(c_desert_stone);
691 MapNode n_ice(c_ice);
692 int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
695 for (s16 z = node_min.Z; z <= node_max.Z; z++)
696 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
698 s16 surface_y = (s16)baseTerrainLevelFromMap(index);
701 if (surface_y > stone_surface_max_y)
702 stone_surface_max_y = surface_y;
704 BiomeV6Type bt = getBiome(v2s16(x, z));
706 // Fill ground with stone
707 const v3s16 &em = vm->m_area.getExtent();
708 u32 i = vm->m_area.index(x, node_min.Y, z);
709 for (s16 y = node_min.Y; y <= node_max.Y; y++) {
710 if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
711 if (y <= surface_y) {
712 vm->m_data[i] = (y >= MGV6_DESERT_STONE_BASE
713 && bt == BT_DESERT) ?
714 n_desert_stone : n_stone;
715 } else if (y <= water_level) {
716 vm->m_data[i] = (y >= MGV6_ICE_BASE
717 && bt == BT_TUNDRA) ?
718 n_ice : n_water_source;
720 vm->m_data[i] = n_air;
723 VoxelArea::add_y(em, i, 1);
727 return stone_surface_max_y;
731 void MapgenV6::addMud()
734 //TimeTaker timer1("add mud");
735 MapNode n_dirt(c_dirt), n_gravel(c_gravel);
736 MapNode n_sand(c_sand), n_desert_sand(c_desert_sand);
740 for (s16 z = node_min.Z; z <= node_max.Z; z++)
741 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
742 // Randomize mud amount
743 s16 mud_add_amount = getMudAmount(index) / 2.0 + 0.5;
746 s16 surface_y = find_stone_level(v2s16(x, z)); /////////////////optimize this!
748 // Handle area not found
749 if (surface_y == vm->m_area.MinEdge.Y - 1)
752 BiomeV6Type bt = getBiome(v2s16(x, z));
753 addnode = (bt == BT_DESERT) ? n_desert_sand : n_dirt;
755 if (bt == BT_DESERT && surface_y + mud_add_amount <= water_level + 1) {
757 } else if (mud_add_amount <= 0) {
758 mud_add_amount = 1 - mud_add_amount;
760 } else if (bt != BT_DESERT && getHaveBeach(index) &&
761 surface_y + mud_add_amount <= water_level + 2) {
765 if ((bt == BT_DESERT || bt == BT_TUNDRA) && surface_y > 20)
766 mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20) / 5);
768 /* If topmost node is grass, change it to mud. It might be if it was
769 // flown to there from a neighboring chunk and then converted.
770 u32 i = vm->m_area.index(x, surface_y, z);
771 if (vm->m_data[i].getContent() == c_dirt_with_grass)
772 vm->m_data[i] = n_dirt;*/
776 const v3s16 &em = vm->m_area.getExtent();
777 s16 y_start = surface_y + 1;
778 u32 i = vm->m_area.index(x, y_start, z);
779 for (s16 y = y_start; y <= node_max.Y; y++) {
780 if (mudcount >= mud_add_amount)
783 vm->m_data[i] = addnode;
786 VoxelArea::add_y(em, i, 1);
792 void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
794 const v3s16 &em = vm->m_area.getExtent();
795 static const v3s16 dirs4[4] = {
796 v3s16(0, 0, 1), // Back
797 v3s16(1, 0, 0), // Right
798 v3s16(0, 0, -1), // Front
799 v3s16(-1, 0, 0), // Left
803 for (s16 k = 0; k < 2; k++) {
804 for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++)
805 for (s16 x = mudflow_minpos; x <= mudflow_maxpos; x++) {
806 // Node column position
808 // Invert coordinates on second iteration to process columns in
809 // opposite order, to avoid a directional bias.
811 p2d = v2s16(node_max.X, node_max.Z) - v2s16(x, z);
813 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x, z);
817 while (y >= node_min.Y) {
819 u32 i = vm->m_area.index(p2d.X, y, p2d.Y);
820 MapNode *n = nullptr;
822 // Find next mud node in mapchunk column
823 for (; y >= node_min.Y; y--) {
825 if (n->getContent() == c_dirt ||
826 n->getContent() == c_dirt_with_grass ||
827 n->getContent() == c_gravel)
830 VoxelArea::add_y(em, i, -1);
833 // No mud found in mapchunk column, process the next column
836 if (n->getContent() == c_dirt || n->getContent() == c_dirt_with_grass) {
837 // Convert dirt_with_grass to dirt
838 n->setContent(c_dirt);
839 // Don't flow mud if the stuff under it is not mud,
840 // to leave at least 1 node of mud.
842 VoxelArea::add_y(em, i2, -1);
843 MapNode *n2 = &vm->m_data[i2];
844 if (n2->getContent() != c_dirt &&
845 n2->getContent() != c_dirt_with_grass)
846 // Find next mud node in column
850 // Check if node above is walkable. If so, cancel
851 // flowing as if node above keeps it in place.
853 VoxelArea::add_y(em, i3, 1);
854 MapNode *n3 = &vm->m_data[i3];
855 if (ndef->get(*n3).walkable)
856 // Find next mud node in column
859 // Drop mud on one side
860 for (const v3s16 &dirp : dirs4) {
863 VoxelArea::add_p(em, i2, dirp);
864 // Check that side is air
865 MapNode *n2 = &vm->m_data[i2];
866 if (ndef->get(*n2).walkable)
869 // Check that under side is air
870 VoxelArea::add_y(em, i2, -1);
871 n2 = &vm->m_data[i2];
872 if (ndef->get(*n2).walkable)
875 // Loop further down until not air
876 s16 y2 = y - 1; // y of i2
877 bool dropped_to_unknown = false;
880 VoxelArea::add_y(em, i2, -1);
881 n2 = &vm->m_data[i2];
882 // If out of area or in ungenerated world
883 if (y2 < full_node_min.Y || n2->getContent() == CONTENT_IGNORE) {
884 dropped_to_unknown = true;
887 } while (!ndef->get(*n2).walkable);
889 if (!dropped_to_unknown) {
890 // Move up one so that we're in air
891 VoxelArea::add_y(em, i2, 1);
892 // Move mud to new place, and if outside mapchunk remove
893 // any decorations above removed or placed mud.
894 moveMud(i, i2, i3, p2d, em);
896 // Done, find next mud node in column
906 void MapgenV6::moveMud(u32 remove_index, u32 place_index,
907 u32 above_remove_index, v2s16 pos, v3s16 em)
909 MapNode n_air(CONTENT_AIR);
910 // Copy mud from old place to new place
911 vm->m_data[place_index] = vm->m_data[remove_index];
912 // Set old place to be air
913 vm->m_data[remove_index] = n_air;
914 // Outside the mapchunk decorations may need to be removed if above removed
915 // mud or if half-buried in placed mud. Placed mud is to the side of pos so
916 // use 'pos.X >= node_max.X' etc.
917 if (pos.X >= node_max.X || pos.X <= node_min.X ||
918 pos.Y >= node_max.Z || pos.Y <= node_min.Z) {
919 // 'above remove' node is above removed mud. If it is not air, water or
920 // 'ignore' it is a decoration that needs removing. Also search upwards
921 // to remove a possible stacked decoration.
922 // Check for 'ignore' because stacked decorations can penetrate into
923 // 'ignore' nodes above the mapchunk.
924 while (vm->m_area.contains(above_remove_index) &&
925 vm->m_data[above_remove_index].getContent() != CONTENT_AIR &&
926 vm->m_data[above_remove_index].getContent() != c_water_source &&
927 vm->m_data[above_remove_index].getContent() != CONTENT_IGNORE) {
928 vm->m_data[above_remove_index] = n_air;
929 VoxelArea::add_y(em, above_remove_index, 1);
931 // Mud placed may have partially-buried a stacked decoration, search
933 VoxelArea::add_y(em, place_index, 1);
934 while (vm->m_area.contains(place_index) &&
935 vm->m_data[place_index].getContent() != CONTENT_AIR &&
936 vm->m_data[place_index].getContent() != c_water_source &&
937 vm->m_data[place_index].getContent() != CONTENT_IGNORE) {
938 vm->m_data[place_index] = n_air;
939 VoxelArea::add_y(em, place_index, 1);
945 void MapgenV6::placeTreesAndJungleGrass()
947 //TimeTaker t("placeTrees");
948 if (node_max.Y < water_level)
951 PseudoRandom grassrandom(blockseed + 53);
952 content_t c_junglegrass = ndef->getId("mapgen_junglegrass");
953 // if we don't have junglegrass, don't place cignore... that's bad
954 if (c_junglegrass == CONTENT_IGNORE)
955 c_junglegrass = CONTENT_AIR;
956 MapNode n_junglegrass(c_junglegrass);
957 const v3s16 &em = vm->m_area.getExtent();
959 // Divide area into parts
961 s16 sidelen = central_area_size.X / div;
962 double area = sidelen * sidelen;
964 // N.B. We must add jungle grass first, since tree leaves will
965 // obstruct the ground, giving us a false ground level
966 for (s16 z0 = 0; z0 < div; z0++)
967 for (s16 x0 = 0; x0 < div; x0++) {
968 // Center position of part of division
970 node_min.X + sidelen / 2 + sidelen * x0,
971 node_min.Z + sidelen / 2 + sidelen * z0
973 // Minimum edge of part of division
975 node_min.X + sidelen * x0,
976 node_min.Z + sidelen * z0
978 // Maximum edge of part of division
980 node_min.X + sidelen + sidelen * x0 - 1,
981 node_min.Z + sidelen + sidelen * z0 - 1
984 // Get biome at center position of part of division
985 BiomeV6Type bt = getBiome(p2d_center);
989 if (bt == BT_JUNGLE || bt == BT_TAIGA || bt == BT_NORMAL) {
990 tree_count = area * getTreeAmount(p2d_center);
998 if (bt == BT_JUNGLE) {
999 float humidity = getHumidity(p2d_center);
1000 u32 grass_count = 5 * humidity * tree_count;
1001 for (u32 i = 0; i < grass_count; i++) {
1002 s16 x = grassrandom.range(p2d_min.X, p2d_max.X);
1003 s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y);
1004 int mapindex = central_area_size.X * (z - node_min.Z)
1006 s16 y = heightmap[mapindex];
1007 if (y < water_level)
1010 u32 vi = vm->m_area.index(x, y, z);
1011 // place on dirt_with_grass, since we know it is exposed to sunlight
1012 if (vm->m_data[vi].getContent() == c_dirt_with_grass) {
1013 VoxelArea::add_y(em, vi, 1);
1014 vm->m_data[vi] = n_junglegrass;
1019 // Put trees in random places on part of division
1020 for (u32 i = 0; i < tree_count; i++) {
1021 s16 x = myrand_range(p2d_min.X, p2d_max.X);
1022 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
1023 int mapindex = central_area_size.X * (z - node_min.Z)
1025 s16 y = heightmap[mapindex];
1026 // Don't make a tree under water level
1027 // Don't make a tree so high that it doesn't fit
1028 if (y < water_level || y > node_max.Y - 6)
1032 // Trees grow only on mud and grass
1034 u32 i = vm->m_area.index(p);
1035 content_t c = vm->m_data[i].getContent();
1037 c != c_dirt_with_grass &&
1038 c != c_dirt_with_snow)
1044 if (bt == BT_JUNGLE) {
1045 treegen::make_jungletree(*vm, p, ndef, myrand());
1046 } else if (bt == BT_TAIGA) {
1047 treegen::make_pine_tree(*vm, p - v3s16(0, 1, 0), ndef, myrand());
1048 } else if (bt == BT_NORMAL) {
1049 bool is_apple_tree = (myrand_range(0, 3) == 0) &&
1050 getHaveAppleTree(v2s16(x, z));
1051 treegen::make_tree(*vm, p, is_apple_tree, ndef, myrand());
1055 //printf("placeTreesAndJungleGrass: %dms\n", t.stop());
1059 void MapgenV6::growGrass() // Add surface nodes
1061 MapNode n_dirt_with_grass(c_dirt_with_grass);
1062 MapNode n_dirt_with_snow(c_dirt_with_snow);
1063 MapNode n_snowblock(c_snowblock);
1064 MapNode n_snow(c_snow);
1065 const v3s16 &em = vm->m_area.getExtent();
1068 for (s16 z = full_node_min.Z; z <= full_node_max.Z; z++)
1069 for (s16 x = full_node_min.X; x <= full_node_max.X; x++, index++) {
1070 // Find the lowest surface to which enough light ends up to make
1071 // grass grow. Basically just wait until not air and not leaves.
1074 u32 i = vm->m_area.index(x, node_max.Y, z);
1076 // Go to ground level
1077 for (y = node_max.Y; y >= full_node_min.Y; y--) {
1078 MapNode &n = vm->m_data[i];
1079 if (ndef->get(n).param_type != CPT_LIGHT ||
1080 ndef->get(n).liquid_type != LIQUID_NONE ||
1081 n.getContent() == c_ice)
1083 VoxelArea::add_y(em, i, -1);
1085 surface_y = (y >= full_node_min.Y) ? y : full_node_min.Y;
1088 BiomeV6Type bt = getBiome(index, v2s16(x, z));
1089 u32 i = vm->m_area.index(x, surface_y, z);
1090 content_t c = vm->m_data[i].getContent();
1091 if (surface_y >= water_level - 20) {
1092 if (bt == BT_TAIGA && c == c_dirt) {
1093 vm->m_data[i] = n_dirt_with_snow;
1094 } else if (bt == BT_TUNDRA) {
1096 vm->m_data[i] = n_snowblock;
1097 VoxelArea::add_y(em, i, -1);
1098 vm->m_data[i] = n_dirt_with_snow;
1099 } else if (c == c_stone && surface_y < node_max.Y) {
1100 VoxelArea::add_y(em, i, 1);
1101 vm->m_data[i] = n_snowblock;
1103 } else if (c == c_dirt) {
1104 vm->m_data[i] = n_dirt_with_grass;
1111 void MapgenV6::generateCaves(int max_stone_y)
1113 float cave_amount = NoisePerlin2D(np_cave, node_min.X, node_min.Y, seed);
1114 int volume_nodes = (node_max.X - node_min.X + 1) *
1115 (node_max.Y - node_min.Y + 1) * MAP_BLOCKSIZE;
1116 cave_amount = MYMAX(0.0, cave_amount);
1117 u32 caves_count = cave_amount * volume_nodes / 50000;
1118 u32 bruises_count = 1;
1119 PseudoRandom ps(blockseed + 21343);
1120 PseudoRandom ps2(blockseed + 1032);
1122 if (ps.range(1, 6) == 1)
1123 bruises_count = ps.range(0, ps.range(0, 2));
1125 if (getBiome(v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
1130 for (u32 i = 0; i < caves_count + bruises_count; i++) {
1131 CavesV6 cave(ndef, &gennotify, water_level, c_water_source, c_lava_source);
1133 bool large_cave = (i >= caves_count);
1134 cave.makeCave(vm, node_min, node_max, &ps, &ps2,
1135 large_cave, max_stone_y, heightmap);