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 s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
346 return baseTerrainLevelFromNoise(p2d) + MGV6_AVERAGE_MUD_AMOUNT;
350 int MapgenV6::getGroundLevelAtPoint(v2s16 p)
352 return baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
356 int MapgenV6::getSpawnLevelAtPoint(v2s16 p)
358 s16 level_at_point = baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
359 if (level_at_point <= water_level ||
360 level_at_point > water_level + 16)
361 return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
363 return level_at_point;
367 //////////////////////// Noise functions
369 float MapgenV6::getMudAmount(v2s16 p)
371 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
372 return getMudAmount(index);
376 bool MapgenV6::getHaveBeach(v2s16 p)
378 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
379 return getHaveBeach(index);
383 BiomeV6Type MapgenV6::getBiome(v2s16 p)
385 int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
386 + (p.X - full_node_min.X);
387 return getBiome(index, p);
391 float MapgenV6::getHumidity(v2s16 p)
393 /*double noise = noise2d_perlin(
394 0.5+(float)p.X/500, 0.5+(float)p.Y/500,
395 seed+72384, 4, 0.66);
396 noise = (noise + 1.0)/2.0;*/
398 int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
399 + (p.X - full_node_min.X);
400 float noise = noise_humidity->result[index];
410 float MapgenV6::getTreeAmount(v2s16 p)
412 /*double noise = noise2d_perlin(
413 0.5+(float)p.X/125, 0.5+(float)p.Y/125,
416 float noise = NoisePerlin2D(np_trees, p.X, p.Y, seed);
417 float zeroval = -0.39;
421 return 0.04 * (noise - zeroval) / (1.0 - zeroval);
425 bool MapgenV6::getHaveAppleTree(v2s16 p)
427 /*is_apple_tree = noise2d_perlin(
428 0.5+(float)p.X/100, 0.5+(float)p.Z/100,
429 data->seed+342902, 3, 0.45) > 0.2;*/
431 float noise = NoisePerlin2D(np_apple_trees, p.X, p.Y, seed);
437 float MapgenV6::getMudAmount(int index)
439 if (spflags & MGV6_FLAT)
440 return MGV6_AVERAGE_MUD_AMOUNT;
442 /*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin(
443 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
444 seed+91013, 3, 0.55));*/
446 return noise_mud->result[index];
450 bool MapgenV6::getHaveBeach(int index)
452 // Determine whether to have sand here
453 /*double sandnoise = noise2d_perlin(
454 0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
455 seed+59420, 3, 0.50);*/
457 float sandnoise = noise_beach->result[index];
458 return (sandnoise > freq_beach);
462 BiomeV6Type MapgenV6::getBiome(int index, v2s16 p)
464 // Just do something very simple as for now
465 /*double d = noise2d_perlin(
466 0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
467 seed+9130, 3, 0.50);*/
469 float d = noise_biome->result[index];
470 float h = noise_humidity->result[index];
472 if (spflags & MGV6_SNOWBIOMES) {
473 float blend = (spflags & MGV6_BIOMEBLEND) ? noise2d(p.X, p.Y, seed) / 40 : 0;
475 if (d > MGV6_FREQ_HOT + blend) {
476 if (h > MGV6_FREQ_JUNGLE + blend)
482 if (d < MGV6_FREQ_SNOW + blend) {
483 if (h > MGV6_FREQ_TAIGA + blend)
495 if ((spflags & MGV6_BIOMEBLEND) && (d > freq_desert - 0.10) &&
496 ((noise2d(p.X, p.Y, seed) + 1.0) > (freq_desert - d) * 20.0))
499 if ((spflags & MGV6_JUNGLES) && h > 0.75)
507 u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
509 s32 x = p.X, y = p.Y, z = p.Z;
510 return (u32)(seed % 0x100000000ULL) + z * 38134234 + y * 42123 + x * 23;
514 //////////////////////// Map generator
516 void MapgenV6::makeChunk(BlockMakeData *data)
519 assert(data->vmanip);
520 assert(data->nodedef);
521 assert(data->blockpos_requested.X >= data->blockpos_min.X &&
522 data->blockpos_requested.Y >= data->blockpos_min.Y &&
523 data->blockpos_requested.Z >= data->blockpos_min.Z);
524 assert(data->blockpos_requested.X <= data->blockpos_max.X &&
525 data->blockpos_requested.Y <= data->blockpos_max.Y &&
526 data->blockpos_requested.Z <= data->blockpos_max.Z);
528 this->generating = true;
529 this->vm = data->vmanip;
530 this->ndef = data->nodedef;
532 // Hack: use minimum block coords for old code that assumes a single block
533 v3s16 blockpos_min = data->blockpos_min;
534 v3s16 blockpos_max = data->blockpos_max;
536 // Area of central chunk
537 node_min = blockpos_min * MAP_BLOCKSIZE;
538 node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
540 // Full allocated area
541 full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
542 full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
544 central_area_size = node_max - node_min + v3s16(1, 1, 1);
545 assert(central_area_size.X == central_area_size.Z);
547 // Create a block-specific seed
548 blockseed = get_blockseed(data->seed, full_node_min);
553 // Maximum height of the stone surface and obstacles.
554 // This is used to guide the cave generation
555 s16 stone_surface_max_y;
557 // Generate general ground level to full area
558 stone_surface_max_y = generateGround();
560 // Create initial heightmap to limit caves
561 updateHeightmap(node_min, node_max);
563 const s16 max_spread_amount = MAP_BLOCKSIZE;
564 // Limit dirt flow area by 1 because mud is flowed into neighbors
565 s16 mudflow_minpos = -max_spread_amount + 1;
566 s16 mudflow_maxpos = central_area_size.X + max_spread_amount - 2;
568 // Loop this part, it will make stuff look older and newer nicely
569 const u32 age_loops = 2;
570 for (u32 i_age = 0; i_age < age_loops; i_age++) { // Aging loop
571 // Make caves (this code is relatively horrible)
572 if (flags & MG_CAVES)
573 generateCaves(stone_surface_max_y);
575 // Add mud to the central chunk
578 // Flow mud away from steep edges
579 if (spflags & MGV6_MUDFLOW)
580 flowMud(mudflow_minpos, mudflow_maxpos);
584 // Update heightmap after mudflow
585 updateHeightmap(node_min, node_max);
588 if ((flags & MG_DUNGEONS) && stone_surface_max_y >= node_min.Y &&
589 full_node_min.Y >= dungeon_ymin && full_node_max.Y <= dungeon_ymax) {
590 u16 num_dungeons = std::fmax(std::floor(
591 NoisePerlin3D(&np_dungeons, node_min.X, node_min.Y, node_min.Z, seed)), 0.0f);
593 if (num_dungeons >= 1) {
594 PseudoRandom ps(blockseed + 4713);
599 dp.num_dungeons = num_dungeons;
600 dp.only_in_ground = true;
601 dp.corridor_len_min = 1;
602 dp.corridor_len_max = 13;
603 dp.num_rooms = ps.range(2, 16);
604 dp.large_room_chance = (ps.range(1, 4) == 1) ? 1 : 0;
607 = NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);
609 if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
610 dp.c_wall = c_desert_stone;
611 dp.c_alt_wall = CONTENT_IGNORE;
612 dp.c_stair = c_stair_desert_stone;
614 dp.diagonal_dirs = true;
615 dp.holesize = v3s16(2, 3, 2);
616 dp.room_size_min = v3s16(6, 9, 6);
617 dp.room_size_max = v3s16(10, 11, 10);
618 dp.room_size_large_min = v3s16(10, 13, 10);
619 dp.room_size_large_max = v3s16(18, 21, 18);
620 dp.notifytype = GENNOTIFY_TEMPLE;
622 dp.c_wall = c_cobble;
623 dp.c_alt_wall = c_mossycobble;
624 dp.c_stair = c_stair_cobble;
626 dp.diagonal_dirs = false;
627 dp.holesize = v3s16(1, 2, 1);
628 dp.room_size_min = v3s16(4, 4, 4);
629 dp.room_size_max = v3s16(8, 6, 8);
630 dp.room_size_large_min = v3s16(8, 8, 8);
631 dp.room_size_large_max = v3s16(16, 16, 16);
632 dp.notifytype = GENNOTIFY_DUNGEON;
635 DungeonGen dgen(ndef, &gennotify, &dp);
636 dgen.generate(vm, blockseed, full_node_min, full_node_max);
640 // Add top and bottom side of water to transforming_liquid queue
641 updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
646 // Generate some trees, and add grass, if a jungle
647 if (spflags & MGV6_TREES)
648 placeTreesAndJungleGrass();
650 // Generate the registered decorations
651 if (flags & MG_DECORATIONS)
652 m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
654 // Generate the registered ores
655 m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
657 // Calculate lighting
658 if (flags & MG_LIGHT)
659 calcLighting(node_min - v3s16(1, 1, 1) * MAP_BLOCKSIZE,
660 node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE,
661 full_node_min, full_node_max);
663 this->generating = false;
667 void MapgenV6::calculateNoise()
671 int fx = full_node_min.X;
672 int fz = full_node_min.Z;
674 if (!(spflags & MGV6_FLAT)) {
675 noise_terrain_base->perlinMap2D_PO(x, 0.5, z, 0.5);
676 noise_terrain_higher->perlinMap2D_PO(x, 0.5, z, 0.5);
677 noise_steepness->perlinMap2D_PO(x, 0.5, z, 0.5);
678 noise_height_select->perlinMap2D_PO(x, 0.5, z, 0.5);
679 noise_mud->perlinMap2D_PO(x, 0.5, z, 0.5);
682 noise_beach->perlinMap2D_PO(x, 0.2, z, 0.7);
684 noise_biome->perlinMap2D_PO(fx, 0.6, fz, 0.2);
685 noise_humidity->perlinMap2D_PO(fx, 0.0, fz, 0.0);
686 // Humidity map does not need range limiting 0 to 1,
687 // only humidity at point does
691 int MapgenV6::generateGround()
693 //TimeTaker timer1("Generating ground level");
694 MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
695 MapNode n_stone(c_stone), n_desert_stone(c_desert_stone);
696 MapNode n_ice(c_ice);
697 int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
700 for (s16 z = node_min.Z; z <= node_max.Z; z++)
701 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
703 s16 surface_y = (s16)baseTerrainLevelFromMap(index);
706 if (surface_y > stone_surface_max_y)
707 stone_surface_max_y = surface_y;
709 BiomeV6Type bt = getBiome(v2s16(x, z));
711 // Fill ground with stone
712 const v3s16 &em = vm->m_area.getExtent();
713 u32 i = vm->m_area.index(x, node_min.Y, z);
714 for (s16 y = node_min.Y; y <= node_max.Y; y++) {
715 if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
716 if (y <= surface_y) {
717 vm->m_data[i] = (y >= MGV6_DESERT_STONE_BASE
718 && bt == BT_DESERT) ?
719 n_desert_stone : n_stone;
720 } else if (y <= water_level) {
721 vm->m_data[i] = (y >= MGV6_ICE_BASE
722 && bt == BT_TUNDRA) ?
723 n_ice : n_water_source;
725 vm->m_data[i] = n_air;
728 VoxelArea::add_y(em, i, 1);
732 return stone_surface_max_y;
736 void MapgenV6::addMud()
739 //TimeTaker timer1("add mud");
740 MapNode n_dirt(c_dirt), n_gravel(c_gravel);
741 MapNode n_sand(c_sand), n_desert_sand(c_desert_sand);
745 for (s16 z = node_min.Z; z <= node_max.Z; z++)
746 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
747 // Randomize mud amount
748 s16 mud_add_amount = getMudAmount(index) / 2.0 + 0.5;
751 s16 surface_y = find_stone_level(v2s16(x, z)); /////////////////optimize this!
753 // Handle area not found
754 if (surface_y == vm->m_area.MinEdge.Y - 1)
757 BiomeV6Type bt = getBiome(v2s16(x, z));
758 addnode = (bt == BT_DESERT) ? n_desert_sand : n_dirt;
760 if (bt == BT_DESERT && surface_y + mud_add_amount <= water_level + 1) {
762 } else if (mud_add_amount <= 0) {
763 mud_add_amount = 1 - mud_add_amount;
765 } else if (bt != BT_DESERT && getHaveBeach(index) &&
766 surface_y + mud_add_amount <= water_level + 2) {
770 if ((bt == BT_DESERT || bt == BT_TUNDRA) && surface_y > 20)
771 mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20) / 5);
773 /* If topmost node is grass, change it to mud. It might be if it was
774 // flown to there from a neighboring chunk and then converted.
775 u32 i = vm->m_area.index(x, surface_y, z);
776 if (vm->m_data[i].getContent() == c_dirt_with_grass)
777 vm->m_data[i] = n_dirt;*/
781 const v3s16 &em = vm->m_area.getExtent();
782 s16 y_start = surface_y + 1;
783 u32 i = vm->m_area.index(x, y_start, z);
784 for (s16 y = y_start; y <= node_max.Y; y++) {
785 if (mudcount >= mud_add_amount)
788 vm->m_data[i] = addnode;
791 VoxelArea::add_y(em, i, 1);
797 void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
799 const v3s16 &em = vm->m_area.getExtent();
800 static const v3s16 dirs4[4] = {
801 v3s16(0, 0, 1), // Back
802 v3s16(1, 0, 0), // Right
803 v3s16(0, 0, -1), // Front
804 v3s16(-1, 0, 0), // Left
808 for (s16 k = 0; k < 2; k++) {
809 for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++)
810 for (s16 x = mudflow_minpos; x <= mudflow_maxpos; x++) {
811 // Node column position
813 // Invert coordinates on second iteration to process columns in
814 // opposite order, to avoid a directional bias.
816 p2d = v2s16(node_max.X, node_max.Z) - v2s16(x, z);
818 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x, z);
822 while (y >= node_min.Y) {
824 u32 i = vm->m_area.index(p2d.X, y, p2d.Y);
825 MapNode *n = nullptr;
827 // Find next mud node in mapchunk column
828 for (; y >= node_min.Y; y--) {
830 if (n->getContent() == c_dirt ||
831 n->getContent() == c_dirt_with_grass ||
832 n->getContent() == c_gravel)
835 VoxelArea::add_y(em, i, -1);
838 // No mud found in mapchunk column, process the next column
841 if (n->getContent() == c_dirt || n->getContent() == c_dirt_with_grass) {
842 // Convert dirt_with_grass to dirt
843 n->setContent(c_dirt);
844 // Don't flow mud if the stuff under it is not mud,
845 // to leave at least 1 node of mud.
847 VoxelArea::add_y(em, i2, -1);
848 MapNode *n2 = &vm->m_data[i2];
849 if (n2->getContent() != c_dirt &&
850 n2->getContent() != c_dirt_with_grass)
851 // Find next mud node in column
855 // Check if node above is walkable. If so, cancel
856 // flowing as if node above keeps it in place.
858 VoxelArea::add_y(em, i3, 1);
859 MapNode *n3 = &vm->m_data[i3];
860 if (ndef->get(*n3).walkable)
861 // Find next mud node in column
864 // Drop mud on one side
865 for (const v3s16 &dirp : dirs4) {
868 VoxelArea::add_p(em, i2, dirp);
869 // Check that side is air
870 MapNode *n2 = &vm->m_data[i2];
871 if (ndef->get(*n2).walkable)
874 // Check that under side is air
875 VoxelArea::add_y(em, i2, -1);
876 n2 = &vm->m_data[i2];
877 if (ndef->get(*n2).walkable)
880 // Loop further down until not air
881 s16 y2 = y - 1; // y of i2
882 bool dropped_to_unknown = false;
885 VoxelArea::add_y(em, i2, -1);
886 n2 = &vm->m_data[i2];
887 // If out of area or in ungenerated world
888 if (y2 < full_node_min.Y || n2->getContent() == CONTENT_IGNORE) {
889 dropped_to_unknown = true;
892 } while (!ndef->get(*n2).walkable);
894 if (!dropped_to_unknown) {
895 // Move up one so that we're in air
896 VoxelArea::add_y(em, i2, 1);
897 // Move mud to new place, and if outside mapchunk remove
898 // any decorations above removed or placed mud.
899 moveMud(i, i2, i3, p2d, em);
901 // Done, find next mud node in column
911 void MapgenV6::moveMud(u32 remove_index, u32 place_index,
912 u32 above_remove_index, v2s16 pos, v3s16 em)
914 MapNode n_air(CONTENT_AIR);
915 // Copy mud from old place to new place
916 vm->m_data[place_index] = vm->m_data[remove_index];
917 // Set old place to be air
918 vm->m_data[remove_index] = n_air;
919 // Outside the mapchunk decorations may need to be removed if above removed
920 // mud or if half-buried in placed mud. Placed mud is to the side of pos so
921 // use 'pos.X >= node_max.X' etc.
922 if (pos.X >= node_max.X || pos.X <= node_min.X ||
923 pos.Y >= node_max.Z || pos.Y <= node_min.Z) {
924 // 'above remove' node is above removed mud. If it is not air, water or
925 // 'ignore' it is a decoration that needs removing. Also search upwards
926 // to remove a possible stacked decoration.
927 // Check for 'ignore' because stacked decorations can penetrate into
928 // 'ignore' nodes above the mapchunk.
929 while (vm->m_area.contains(above_remove_index) &&
930 vm->m_data[above_remove_index].getContent() != CONTENT_AIR &&
931 vm->m_data[above_remove_index].getContent() != c_water_source &&
932 vm->m_data[above_remove_index].getContent() != CONTENT_IGNORE) {
933 vm->m_data[above_remove_index] = n_air;
934 VoxelArea::add_y(em, above_remove_index, 1);
936 // Mud placed may have partially-buried a stacked decoration, search
938 VoxelArea::add_y(em, place_index, 1);
939 while (vm->m_area.contains(place_index) &&
940 vm->m_data[place_index].getContent() != CONTENT_AIR &&
941 vm->m_data[place_index].getContent() != c_water_source &&
942 vm->m_data[place_index].getContent() != CONTENT_IGNORE) {
943 vm->m_data[place_index] = n_air;
944 VoxelArea::add_y(em, place_index, 1);
950 void MapgenV6::placeTreesAndJungleGrass()
952 //TimeTaker t("placeTrees");
953 if (node_max.Y < water_level)
956 PseudoRandom grassrandom(blockseed + 53);
957 content_t c_junglegrass = ndef->getId("mapgen_junglegrass");
958 // if we don't have junglegrass, don't place cignore... that's bad
959 if (c_junglegrass == CONTENT_IGNORE)
960 c_junglegrass = CONTENT_AIR;
961 MapNode n_junglegrass(c_junglegrass);
962 const v3s16 &em = vm->m_area.getExtent();
964 // Divide area into parts
966 s16 sidelen = central_area_size.X / div;
967 double area = sidelen * sidelen;
969 // N.B. We must add jungle grass first, since tree leaves will
970 // obstruct the ground, giving us a false ground level
971 for (s16 z0 = 0; z0 < div; z0++)
972 for (s16 x0 = 0; x0 < div; x0++) {
973 // Center position of part of division
975 node_min.X + sidelen / 2 + sidelen * x0,
976 node_min.Z + sidelen / 2 + sidelen * z0
978 // Minimum edge of part of division
980 node_min.X + sidelen * x0,
981 node_min.Z + sidelen * z0
983 // Maximum edge of part of division
985 node_min.X + sidelen + sidelen * x0 - 1,
986 node_min.Z + sidelen + sidelen * z0 - 1
989 // Get biome at center position of part of division
990 BiomeV6Type bt = getBiome(p2d_center);
994 if (bt == BT_JUNGLE || bt == BT_TAIGA || bt == BT_NORMAL) {
995 tree_count = area * getTreeAmount(p2d_center);
1003 if (bt == BT_JUNGLE) {
1004 float humidity = getHumidity(p2d_center);
1005 u32 grass_count = 5 * humidity * tree_count;
1006 for (u32 i = 0; i < grass_count; i++) {
1007 s16 x = grassrandom.range(p2d_min.X, p2d_max.X);
1008 s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y);
1009 int mapindex = central_area_size.X * (z - node_min.Z)
1011 s16 y = heightmap[mapindex];
1012 if (y < water_level)
1015 u32 vi = vm->m_area.index(x, y, z);
1016 // place on dirt_with_grass, since we know it is exposed to sunlight
1017 if (vm->m_data[vi].getContent() == c_dirt_with_grass) {
1018 VoxelArea::add_y(em, vi, 1);
1019 vm->m_data[vi] = n_junglegrass;
1024 // Put trees in random places on part of division
1025 for (u32 i = 0; i < tree_count; i++) {
1026 s16 x = myrand_range(p2d_min.X, p2d_max.X);
1027 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
1028 int mapindex = central_area_size.X * (z - node_min.Z)
1030 s16 y = heightmap[mapindex];
1031 // Don't make a tree under water level
1032 // Don't make a tree so high that it doesn't fit
1033 if (y < water_level || y > node_max.Y - 6)
1037 // Trees grow only on mud and grass
1039 u32 i = vm->m_area.index(p);
1040 content_t c = vm->m_data[i].getContent();
1042 c != c_dirt_with_grass &&
1043 c != c_dirt_with_snow)
1049 if (bt == BT_JUNGLE) {
1050 treegen::make_jungletree(*vm, p, ndef, myrand());
1051 } else if (bt == BT_TAIGA) {
1052 treegen::make_pine_tree(*vm, p - v3s16(0, 1, 0), ndef, myrand());
1053 } else if (bt == BT_NORMAL) {
1054 bool is_apple_tree = (myrand_range(0, 3) == 0) &&
1055 getHaveAppleTree(v2s16(x, z));
1056 treegen::make_tree(*vm, p, is_apple_tree, ndef, myrand());
1060 //printf("placeTreesAndJungleGrass: %dms\n", t.stop());
1064 void MapgenV6::growGrass() // Add surface nodes
1066 MapNode n_dirt_with_grass(c_dirt_with_grass);
1067 MapNode n_dirt_with_snow(c_dirt_with_snow);
1068 MapNode n_snowblock(c_snowblock);
1069 MapNode n_snow(c_snow);
1070 const v3s16 &em = vm->m_area.getExtent();
1073 for (s16 z = full_node_min.Z; z <= full_node_max.Z; z++)
1074 for (s16 x = full_node_min.X; x <= full_node_max.X; x++, index++) {
1075 // Find the lowest surface to which enough light ends up to make
1076 // grass grow. Basically just wait until not air and not leaves.
1079 u32 i = vm->m_area.index(x, node_max.Y, z);
1081 // Go to ground level
1082 for (y = node_max.Y; y >= full_node_min.Y; y--) {
1083 MapNode &n = vm->m_data[i];
1084 if (ndef->get(n).param_type != CPT_LIGHT ||
1085 ndef->get(n).liquid_type != LIQUID_NONE ||
1086 n.getContent() == c_ice)
1088 VoxelArea::add_y(em, i, -1);
1090 surface_y = (y >= full_node_min.Y) ? y : full_node_min.Y;
1093 BiomeV6Type bt = getBiome(index, v2s16(x, z));
1094 u32 i = vm->m_area.index(x, surface_y, z);
1095 content_t c = vm->m_data[i].getContent();
1096 if (surface_y >= water_level - 20) {
1097 if (bt == BT_TAIGA && c == c_dirt) {
1098 vm->m_data[i] = n_dirt_with_snow;
1099 } else if (bt == BT_TUNDRA) {
1101 vm->m_data[i] = n_snowblock;
1102 VoxelArea::add_y(em, i, -1);
1103 vm->m_data[i] = n_dirt_with_snow;
1104 } else if (c == c_stone && surface_y < node_max.Y) {
1105 VoxelArea::add_y(em, i, 1);
1106 vm->m_data[i] = n_snowblock;
1108 } else if (c == c_dirt) {
1109 vm->m_data[i] = n_dirt_with_grass;
1116 void MapgenV6::generateCaves(int max_stone_y)
1118 float cave_amount = NoisePerlin2D(np_cave, node_min.X, node_min.Y, seed);
1119 int volume_nodes = (node_max.X - node_min.X + 1) *
1120 (node_max.Y - node_min.Y + 1) * MAP_BLOCKSIZE;
1121 cave_amount = MYMAX(0.0, cave_amount);
1122 u32 caves_count = cave_amount * volume_nodes / 50000;
1123 u32 bruises_count = 1;
1124 PseudoRandom ps(blockseed + 21343);
1125 PseudoRandom ps2(blockseed + 1032);
1127 if (ps.range(1, 6) == 1)
1128 bruises_count = ps.range(0, ps.range(0, 2));
1130 if (getBiome(v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
1135 for (u32 i = 0; i < caves_count + bruises_count; i++) {
1136 CavesV6 cave(ndef, &gennotify, water_level, c_water_source, c_lava_source);
1138 bool large_cave = (i >= caves_count);
1139 cave.makeCave(vm, node_min, node_max, &ps, &ps2,
1140 large_cave, max_stone_y, heightmap);