// This keeps us from having to maintain two similar sets of // complicated code to determine ground level. float MapgenValleys::terrainLevelFromNoise(TerrainNoise *tn) { // The square function changes the behaviour of this noise: // very often small, and sometimes very high. float valley_d = MYSQUARE(*tn->valley); // valley_d is here because terrain is generally higher where valleys // are deep (mountains). base represents the height of the // rivers, most of the surface is above. float base = tn->terrain_height + valley_d; // "river" represents the distance from the river, in arbitrary units. float river = fabs(*tn->rivers) - river_size_factor; // Use the curve of the function 1-exp(-(x/a)^2) to model valleys. // Making "a" vary (0 < a <= 1) changes the shape of the valleys. // Try it with a geometry software ! // (here x = "river" and a = valley_profile). // "valley" represents the height of the terrain, from the rivers. { float t = river / tn->valley_profile; *tn->valley = valley_d * (1.f - exp(- MYSQUARE(t))); } // approximate height of the terrain at this point float mount = base + *tn->valley; *tn->slope *= *tn->valley; // Rivers are placed where "river" is negative, so where the original // noise value is close to zero. // Base ground is returned as rivers since it's basically the water table. *tn->rivers = base; if (river < 0.f) { // Use the the function -sqrt(1-x^2) which models a circle. float depth; { float t = river / river_size_factor + 1; depth = (river_depth_bed * sqrt(MYMAX(0, 1.f - MYSQUARE(t)))); } // base - depth : height of the bottom of the river // water_level - 3 : don't make rivers below 3 nodes under the surface // We use three because that's as low as the swamp biomes go. // There is no logical equivalent to this using rangelim. mount = MYMIN(MYMAX(base - depth, (float)(water_level - 3)), mount); // Slope has no influence on rivers. *tn->slope = 0.f; } return mount; }
void MapgenValleys::generateCaves(s16 max_stone_y) { if (max_stone_y < node_min.Y) return; noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); PseudoRandom ps(blockseed + 72202); MapNode n_air(CONTENT_AIR); MapNode n_lava(c_lava_source); MapNode n_water(c_river_water_source); v3s16 em = vm->m_area.getExtent(); // Cave blend distance near YMIN, YMAX const float massive_cave_blend = 128.f; // noise threshold for massive caves const float massive_cave_threshold = 0.6f; // mct: 1 = small rare caves, 0.5 1/3rd ground volume, 0 = 1/2 ground volume. float yblmin = -map_gen_limit + massive_cave_blend * 1.5f; float yblmax = massive_cave_depth - massive_cave_blend * 1.5f; bool made_a_big_one = false; // Cache the tcave values as they only vary by altitude. if (node_max.Y <= massive_cave_depth) { noise_massive_caves->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) { float tcave = massive_cave_threshold; if (y < yblmin) { float t = (yblmin - y) / massive_cave_blend; tcave += MYSQUARE(t); } else if (y > yblmax) { float t = (y - yblmax) / massive_cave_blend; tcave += MYSQUARE(t); } tcave_cache[y - node_min.Y + 1] = tcave; } } // lava_depth varies between one and ten as you approach // the bottom of the world. s16 lava_depth = ceil((lava_max_height - node_min.Y + 1) * 10.f / map_gen_limit); // This allows random lava spawns to be less common at the surface. s16 lava_chance = MYCUBE(lava_features_lim) * lava_depth; // water_depth varies between ten and one on the way down. s16 water_depth = ceil((map_gen_limit - abs(node_min.Y) + 1) * 10.f / map_gen_limit); // This allows random water spawns to be more common at the surface. s16 water_chance = MYCUBE(water_features_lim) * water_depth; // Reduce the odds of overflows even further. if (node_max.Y > water_level) { lava_chance /= 5; water_chance /= 5; } u32 index_2d = 0; u32 index_3d = 0; for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) { Biome *biome = (Biome *)bmgr->getRaw(biomemap[index_2d]); bool air_above = false; bool underground = false; u32 index_data = vm->m_area.index(x, node_max.Y + 1, z); index_3d = (z - node_min.Z) * zstride + (csize.Y + 1) * ystride + (x - node_min.X); // Dig caves on down loop to check for air above. for (s16 y = node_max.Y + 1; y >= node_min.Y - 1; y--, index_3d -= ystride, vm->m_area.add_y(em, index_data, -1)) { float terrain = noise_terrain_height->result[index_2d]; // Saves some time. if (y > terrain + 10) { air_above = true; continue; } else if (y < terrain - 40) { underground = true; } // Dig massive caves. if (node_max.Y <= massive_cave_depth && noise_massive_caves->result[index_3d] > tcave_cache[y - node_min.Y + 1]) { vm->m_data[index_data] = n_air; made_a_big_one = true; } content_t c = vm->m_data[index_data].getContent(); float d1 = contour(noise_cave1->result[index_3d]); float d2 = contour(noise_cave2->result[index_3d]); // River water is not set as ground content // in the default game. This can produce strange results // when a cave undercuts a river. However, that's not for // the mapgen to correct. Fix it in lua. if (c == CONTENT_AIR) { air_above = true; } else if (d1 * d2 > 0.3f && ndef->get(c).is_ground_content) { // in a cave vm->m_data[index_data] = n_air; air_above = true; } else if (air_above && (c == biome->c_filler || c == biome->c_stone)) { // at the cave floor s16 sr = ps.range(0,39); u32 j = index_data; vm->m_area.add_y(em, j, 1); if (sr > terrain - y) { // Put dirt in caves near the surface. if (underground) vm->m_data[index_data] = MapNode(biome->c_filler); else vm->m_data[index_data] = MapNode(biome->c_top); } else if (sr < 3 && underground) { sr = abs(ps.next()); if (lava_features_lim > 0 && y <= lava_max_height && c == biome->c_stone && sr < lava_chance) vm->m_data[j] = n_lava; sr -= lava_chance; // If sr < 0 then we should have already placed lava -- // don't immediately dump water on it. if (water_features_lim > 0 && y <= cave_water_max_height && sr >= 0 && sr < water_chance) vm->m_data[j] = n_water; } air_above = false; underground = true; } else if (c == biome->c_filler || c == biome->c_stone) { air_above = false; underground = true; } else { air_above = false; } } } if (node_max.Y <= large_cave_depth && (!made_a_big_one)) { u32 bruises_count = ps.range(0, 2); for (u32 i = 0; i < bruises_count; i++) { CaveV5 cave(this, &ps); cave.makeCave(node_min, node_max, max_stone_y); } } }
void MapgenValleys::generateCaves(s16 max_stone_y, s16 large_cave_depth) { if (max_stone_y < node_min.Y) return; noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); PseudoRandom ps(blockseed + 72202); MapNode n_air(CONTENT_AIR); MapNode n_lava(c_lava_source); MapNode n_water(c_river_water_source); v3s16 em = vm->m_area.getExtent(); // Cave blend distance near YMIN, YMAX const float massive_cave_blend = 128.f; // noise threshold for massive caves const float massive_cave_threshold = 0.6f; // mct: 1 = small rare caves, 0.5 1/3rd ground volume, 0 = 1/2 ground volume. float yblmin = -map_gen_limit + massive_cave_blend * 1.5f; float yblmax = massive_cave_depth - massive_cave_blend * 1.5f; bool made_a_big_one = false; // Cache the tcave values as they only vary by altitude. if (node_max.Y <= massive_cave_depth) { noise_massive_caves->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); for (s16 y = node_min.Y - 1; y <= node_max.Y; y++) { float tcave = massive_cave_threshold; if (y < yblmin) { float t = (yblmin - y) / massive_cave_blend; tcave += MYSQUARE(t); } else if (y > yblmax) { float t = (y - yblmax) / massive_cave_blend; tcave += MYSQUARE(t); } tcave_cache[y - node_min.Y + 1] = tcave; } } // lava_depth varies between one and ten as you approach // the bottom of the world. s16 lava_depth = ceil((lava_max_height - node_min.Y + 1) * 10.f / map_gen_limit); // This allows random lava spawns to be less common at the surface. s16 lava_chance = MYCUBE(lava_features_lim) * lava_depth; // water_depth varies between ten and one on the way down. s16 water_depth = ceil((map_gen_limit - abs(node_min.Y) + 1) * 10.f / map_gen_limit); // This allows random water spawns to be more common at the surface. s16 water_chance = MYCUBE(water_features_lim) * water_depth; // Reduce the odds of overflows even further. if (node_max.Y > water_level) { lava_chance /= 3; water_chance /= 3; } u32 index_2d = 0; for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) { Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index_2d]); bool tunnel_air_above = false; bool is_under_river = false; bool underground = false; u32 index_data = vm->m_area.index(x, node_max.Y, z); u32 index_3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride + (x - node_min.X); // Dig caves on down loop to check for air above. // Don't excavate the overgenerated stone at node_max.Y + 1, // this creates a 'roof' over the tunnel, preventing light in // tunnels at mapchunk borders when generating mapchunks upwards. // This 'roof' is removed when the mapchunk above is generated. for (s16 y = node_max.Y; y >= node_min.Y - 1; y--, index_3d -= ystride, vm->m_area.add_y(em, index_data, -1)) { float terrain = noise_terrain_height->result[index_2d]; // Saves some time. if (y > terrain + 10) continue; else if (y < terrain - 40) underground = true; // Dig massive caves. if (node_max.Y <= massive_cave_depth && noise_massive_caves->result[index_3d] > tcave_cache[y - node_min.Y + 1]) { vm->m_data[index_data] = n_air; made_a_big_one = true; continue; } content_t c = vm->m_data[index_data].getContent(); // Detect river water to place riverbed nodes in tunnels if (c == biome->c_river_water) is_under_river = true; float d1 = contour(noise_cave1->result[index_3d]); float d2 = contour(noise_cave2->result[index_3d]); if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) { // in a tunnel vm->m_data[index_data] = n_air; tunnel_air_above = true; } else if (c == biome->c_filler || c == biome->c_stone) { if (tunnel_air_above) { // at the tunnel floor s16 sr = ps.range(0, 39); u32 j = index_data; vm->m_area.add_y(em, j, 1); if (sr > terrain - y) { // Put biome nodes in tunnels near the surface if (is_under_river) vm->m_data[index_data] = MapNode(biome->c_riverbed); else if (underground) vm->m_data[index_data] = MapNode(biome->c_filler); else vm->m_data[index_data] = MapNode(biome->c_top); } else if (sr < 3 && underground) { sr = abs(ps.next()); if (lava_features_lim > 0 && y <= lava_max_height && c == biome->c_stone && sr < lava_chance) vm->m_data[j] = n_lava; sr -= lava_chance; // If sr < 0 then we should have already placed lava -- // don't immediately dump water on it. if (water_features_lim > 0 && y <= cave_water_max_height && sr >= 0 && sr < water_chance) vm->m_data[j] = n_water; } } tunnel_air_above = false; underground = true; } else { tunnel_air_above = false; } } } if (node_max.Y <= large_cave_depth && !made_a_big_one) { u32 bruises_count = ps.range(0, 2); for (u32 i = 0; i < bruises_count; i++) { CavesRandomWalk cave(ndef, &gennotify, seed, water_level, c_water_source, c_lava_source); cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y, heightmap); } } }