3 Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2010-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
5 Copyright (C) 2015-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.
22 #include "util/numeric.h"
26 #include "mapgen_v5.h"
27 #include "mapgen_v6.h"
28 #include "mapgen_v7.h"
32 static NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0);
36 //// CavesNoiseIntersection
39 CavesNoiseIntersection::CavesNoiseIntersection(
40 const NodeDefManager *nodedef, BiomeManager *biomemgr, v3s16 chunksize,
41 NoiseParams *np_cave1, NoiseParams *np_cave2, s32 seed, float cave_width)
50 m_cave_width = cave_width;
52 m_ystride = m_csize.X;
53 m_zstride_1d = m_csize.X * (m_csize.Y + 1);
55 // Noises are created using 1-down overgeneration
56 // A Nx-by-1-by-Nz-sized plane is at the bottom of the desired for
57 // re-carving the solid overtop placed for blocking sunlight
58 noise_cave1 = new Noise(np_cave1, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
59 noise_cave2 = new Noise(np_cave2, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
63 CavesNoiseIntersection::~CavesNoiseIntersection()
70 void CavesNoiseIntersection::generateCaves(MMVManip *vm,
71 v3s16 nmin, v3s16 nmax, u8 *biomemap)
76 noise_cave1->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
77 noise_cave2->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
79 const v3s16 &em = vm->m_area.getExtent();
80 u32 index2d = 0; // Biomemap index
82 for (s16 z = nmin.Z; z <= nmax.Z; z++)
83 for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) {
84 bool column_is_open = false; // Is column open to overground
85 bool is_under_river = false; // Is column under river water
86 bool is_under_tunnel = false; // Is tunnel or is under tunnel
87 bool is_top_filler_above = false; // Is top or filler above node
88 // Indexes at column top
89 u32 vi = vm->m_area.index(x, nmax.Y, z);
90 u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride +
91 (x - nmin.X); // 3D noise index
93 Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index2d]);
94 u16 depth_top = biome->depth_top;
95 u16 base_filler = depth_top + biome->depth_filler;
96 u16 depth_riverbed = biome->depth_riverbed;
98 // Don't excavate the overgenerated stone at nmax.Y + 1,
99 // this creates a 'roof' over the tunnel, preventing light in
100 // tunnels at mapchunk borders when generating mapchunks upwards.
101 // This 'roof' is removed when the mapchunk above is generated.
102 for (s16 y = nmax.Y; y >= nmin.Y - 1; y--,
103 index3d -= m_ystride,
104 VoxelArea::add_y(em, vi, -1)) {
105 content_t c = vm->m_data[vi].getContent();
107 if (c == CONTENT_AIR || c == biome->c_water_top ||
108 c == biome->c_water) {
109 column_is_open = true;
110 is_top_filler_above = false;
114 if (c == biome->c_river_water) {
115 column_is_open = true;
116 is_under_river = true;
117 is_top_filler_above = false;
122 float d1 = contour(noise_cave1->result[index3d]);
123 float d2 = contour(noise_cave2->result[index3d]);
125 if (d1 * d2 > m_cave_width && m_ndef->get(c).is_ground_content) {
126 // In tunnel and ground content, excavate
127 vm->m_data[vi] = MapNode(CONTENT_AIR);
128 is_under_tunnel = true;
129 // If tunnel roof is top or filler, replace with stone
130 if (is_top_filler_above)
131 vm->m_data[vi + em.X] = MapNode(biome->c_stone);
132 is_top_filler_above = false;
133 } else if (column_is_open && is_under_tunnel &&
134 (c == biome->c_stone || c == biome->c_filler)) {
135 // Tunnel entrance floor, place biome surface nodes
136 if (is_under_river) {
137 if (nplaced < depth_riverbed) {
138 vm->m_data[vi] = MapNode(biome->c_riverbed);
139 is_top_filler_above = true;
142 // Disable top/filler placement
143 column_is_open = false;
144 is_under_river = false;
145 is_under_tunnel = false;
147 } else if (nplaced < depth_top) {
148 vm->m_data[vi] = MapNode(biome->c_top);
149 is_top_filler_above = true;
151 } else if (nplaced < base_filler) {
152 vm->m_data[vi] = MapNode(biome->c_filler);
153 is_top_filler_above = true;
156 // Disable top/filler placement
157 column_is_open = false;
158 is_under_tunnel = false;
161 // Not tunnel or tunnel entrance floor
162 // Check node for possible replacing with stone for tunnel roof
163 if (c == biome->c_top || c == biome->c_filler)
164 is_top_filler_above = true;
166 column_is_open = false;
177 CavernsNoise::CavernsNoise(
178 const NodeDefManager *nodedef, v3s16 chunksize, NoiseParams *np_cavern,
179 s32 seed, float cavern_limit, float cavern_taper, float cavern_threshold)
186 m_cavern_limit = cavern_limit;
187 m_cavern_taper = cavern_taper;
188 m_cavern_threshold = cavern_threshold;
190 m_ystride = m_csize.X;
191 m_zstride_1d = m_csize.X * (m_csize.Y + 1);
193 // Noise is created using 1-down overgeneration
194 // A Nx-by-1-by-Nz-sized plane is at the bottom of the desired for
195 // re-carving the solid overtop placed for blocking sunlight
196 noise_cavern = new Noise(np_cavern, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
198 c_water_source = m_ndef->getId("mapgen_water_source");
199 if (c_water_source == CONTENT_IGNORE)
200 c_water_source = CONTENT_AIR;
202 c_lava_source = m_ndef->getId("mapgen_lava_source");
203 if (c_lava_source == CONTENT_IGNORE)
204 c_lava_source = CONTENT_AIR;
208 CavernsNoise::~CavernsNoise()
214 bool CavernsNoise::generateCaverns(MMVManip *vm, v3s16 nmin, v3s16 nmax)
219 noise_cavern->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
221 // Cache cavern_amp values
222 float *cavern_amp = new float[m_csize.Y + 1];
223 u8 cavern_amp_index = 0; // Index zero at column top
224 for (s16 y = nmax.Y; y >= nmin.Y - 1; y--, cavern_amp_index++) {
225 cavern_amp[cavern_amp_index] =
226 MYMIN((m_cavern_limit - y) / (float)m_cavern_taper, 1.0f);
230 bool near_cavern = false;
231 const v3s16 &em = vm->m_area.getExtent();
234 for (s16 z = nmin.Z; z <= nmax.Z; z++)
235 for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) {
236 // Reset cave_amp index to column top
237 cavern_amp_index = 0;
238 // Initial voxelmanip index at column top
239 u32 vi = vm->m_area.index(x, nmax.Y, z);
240 // Initial 3D noise index at column top
241 u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride +
243 // Don't excavate the overgenerated stone at node_max.Y + 1,
244 // this creates a 'roof' over the cavern, preventing light in
245 // caverns at mapchunk borders when generating mapchunks upwards.
246 // This 'roof' is excavated when the mapchunk above is generated.
247 for (s16 y = nmax.Y; y >= nmin.Y - 1; y--,
248 index3d -= m_ystride,
249 VoxelArea::add_y(em, vi, -1),
250 cavern_amp_index++) {
251 content_t c = vm->m_data[vi].getContent();
252 float n_absamp_cavern = std::fabs(noise_cavern->result[index3d]) *
253 cavern_amp[cavern_amp_index];
254 // Disable CavesRandomWalk at a safe distance from caverns
255 // to avoid excessively spreading liquids in caverns.
256 if (n_absamp_cavern > m_cavern_threshold - 0.1f) {
258 if (n_absamp_cavern > m_cavern_threshold &&
259 m_ndef->get(c).is_ground_content)
260 vm->m_data[vi] = MapNode(CONTENT_AIR);
275 CavesRandomWalk::CavesRandomWalk(
276 const NodeDefManager *ndef,
277 GenerateNotifier *gennotify,
280 content_t water_source,
281 content_t lava_source,
288 this->gennotify = gennotify;
290 this->water_level = water_level;
291 this->np_caveliquids = &nparams_caveliquids;
292 this->lava_depth = lava_depth;
293 this->bmgn = biomegen;
295 c_water_source = water_source;
296 if (c_water_source == CONTENT_IGNORE)
297 c_water_source = ndef->getId("mapgen_water_source");
298 if (c_water_source == CONTENT_IGNORE)
299 c_water_source = CONTENT_AIR;
301 c_lava_source = lava_source;
302 if (c_lava_source == CONTENT_IGNORE)
303 c_lava_source = ndef->getId("mapgen_lava_source");
304 if (c_lava_source == CONTENT_IGNORE)
305 c_lava_source = CONTENT_AIR;
309 void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax,
310 PseudoRandom *ps, bool is_large_cave, int max_stone_height, s16 *heightmap)
317 this->node_min = nmin;
318 this->node_max = nmax;
319 this->heightmap = heightmap;
320 this->large_cave = is_large_cave;
322 this->ystride = nmax.X - nmin.X + 1;
324 // Set initial parameters from randomness
325 int dswitchint = ps->range(1, 14);
326 flooded = ps->range(1, 2) == 2;
329 part_max_length_rs = ps->range(2, 4);
330 tunnel_routepoints = ps->range(5, ps->range(15, 30));
331 min_tunnel_diameter = 5;
332 max_tunnel_diameter = ps->range(7, ps->range(8, 24));
334 part_max_length_rs = ps->range(2, 9);
335 tunnel_routepoints = ps->range(10, ps->range(15, 30));
336 min_tunnel_diameter = 2;
337 max_tunnel_diameter = ps->range(2, 6);
340 large_cave_is_flat = (ps->range(0, 1) == 0);
342 main_direction = v3f(0, 0, 0);
344 // Allowed route area size in nodes
345 ar = node_max - node_min + v3s16(1, 1, 1);
346 // Area starting point in nodes
350 //(this should be more than the maximum radius of the tunnel)
351 const s16 insure = 10;
352 s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1);
353 ar += v3s16(1, 0, 1) * more * 2;
354 of -= v3s16(1, 0, 1) * more;
357 // Allow half a diameter + 7 over stone surface
358 route_y_max = -of.Y + max_stone_height + max_tunnel_diameter / 2 + 7;
360 // Limit maximum to area
361 route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
365 if (node_min.Y < water_level && node_max.Y > water_level) {
366 minpos = water_level - max_tunnel_diameter / 3 - of.Y;
367 route_y_max = water_level + max_tunnel_diameter / 3 - of.Y;
369 route_y_min = ps->range(minpos, minpos + max_tunnel_diameter);
370 route_y_min = rangelim(route_y_min, 0, route_y_max);
373 s16 route_start_y_min = route_y_min;
374 s16 route_start_y_max = route_y_max;
376 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
377 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
379 // Randomize starting position
380 orp.Z = (float)(ps->next() % ar.Z) + 0.5f;
381 orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f;
382 orp.X = (float)(ps->next() % ar.X) + 0.5f;
384 // Add generation notify begin event
386 v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
387 GenNotifyType notifytype = large_cave ?
388 GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
389 gennotify->addEvent(notifytype, abs_pos);
392 // Generate some tunnel starting from orp
393 for (u16 j = 0; j < tunnel_routepoints; j++)
394 makeTunnel(j % dswitchint == 0);
396 // Add generation notify end event
398 v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
399 GenNotifyType notifytype = large_cave ?
400 GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
401 gennotify->addEvent(notifytype, abs_pos);
406 void CavesRandomWalk::makeTunnel(bool dirswitch)
408 if (dirswitch && !large_cave) {
409 main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10;
410 main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30;
411 main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10;
413 main_direction *= (float)ps->range(0, 10) / 10;
417 s16 min_d = min_tunnel_diameter;
418 s16 max_d = max_tunnel_diameter;
419 rs = ps->range(min_d, max_d);
420 s16 rs_part_max_length_rs = rs * part_max_length_rs;
425 rs_part_max_length_rs,
426 rs_part_max_length_rs / 2,
427 rs_part_max_length_rs
431 rs_part_max_length_rs,
432 ps->range(1, rs_part_max_length_rs),
433 rs_part_max_length_rs
438 // Jump downward sometimes
439 if (!large_cave && ps->range(0, 12) == 0) {
440 vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2;
441 vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y;
442 vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2;
444 vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2;
445 vec.Y = (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2;
446 vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2;
449 // Do not make caves that are above ground.
450 // It is only necessary to check the startpoint and endpoint.
451 v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2;
452 v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1;
453 if (isPosAboveSurface(p1) || isPosAboveSurface(p2))
456 vec += main_direction;
461 else if (rp.X >= ar.X)
464 if (rp.Y < route_y_min)
466 else if (rp.Y >= route_y_max)
467 rp.Y = route_y_max - 1;
471 else if (rp.Z >= ar.Z)
476 float veclen = vec.getLength();
480 // Every second section is rough
481 bool randomize_xz = (ps->range(1, 2) == 1);
484 for (float f = 0.f; f < 1.0f; f += 1.0f / veclen)
485 carveRoute(vec, f, randomize_xz);
491 void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz)
493 MapNode airnode(CONTENT_AIR);
494 MapNode waternode(c_water_source);
495 MapNode lavanode(c_lava_source);
497 v3s16 startp(orp.X, orp.Y, orp.Z);
500 v3f fp = orp + vec * f;
501 fp.X += 0.1f * ps->range(-10, 10);
502 fp.Z += 0.1f * ps->range(-10, 10);
503 v3s16 cp(fp.X, fp.Y, fp.Z);
505 // Get biome at 'cp + of', the absolute centre point of this route
506 v3s16 cpabs = cp + of;
507 MapNode liquidnode = CONTENT_IGNORE;
510 Biome *biome = nullptr;
511 if (cpabs.X < node_min.X || cpabs.X > node_max.X ||
512 cpabs.Z < node_min.Z || cpabs.Z > node_max.Z)
513 // Point is outside heat and humidity noise maps so use point noise
515 biome = (Biome *)bmgn->calcBiomeAtPoint(cpabs);
517 // Point is inside heat and humidity noise maps so use them
518 biome = (Biome *)bmgn->getBiomeAtPoint(cpabs);
520 if (biome->c_cave_liquid != CONTENT_IGNORE)
521 liquidnode = biome->c_cave_liquid;
524 if (liquidnode == CONTENT_IGNORE) {
525 // Fallback to classic behaviour using point 'startp'
526 float nval = NoisePerlin3D(np_caveliquids, startp.X,
527 startp.Y, startp.Z, seed);
528 liquidnode = (nval < 0.40f && node_max.Y < lava_depth) ?
529 lavanode : waternode;
535 d0 += ps->range(-1, 1);
536 d1 += ps->range(-1, 1);
539 bool flat_cave_floor = !large_cave && ps->range(0, 2) == 2;
541 for (s16 z0 = d0; z0 <= d1; z0++) {
542 s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
543 for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
544 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
546 s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
548 for (s16 y0 = -si2; y0 <= si2; y0++) {
549 // Make better floors in small caves
550 if (flat_cave_floor && y0 <= -rs / 2 && rs <= 7)
553 if (large_cave_is_flat) {
554 // Make large caves not so tall
555 if (rs > 7 && abs(y0) >= rs / 3)
559 v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
562 if (!vm->m_area.contains(p))
565 u32 i = vm->m_area.index(p);
566 content_t c = vm->m_data[i].getContent();
567 if (!ndef->get(c).is_ground_content)
571 int full_ymin = node_min.Y - MAP_BLOCKSIZE;
572 int full_ymax = node_max.Y + MAP_BLOCKSIZE;
574 if (flooded && full_ymin < water_level && full_ymax > water_level)
575 vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
576 else if (flooded && full_ymax < water_level)
577 vm->m_data[i] = (p.Y < startp.Y - 4) ? liquidnode : airnode;
579 vm->m_data[i] = airnode;
581 vm->m_data[i] = airnode;
582 vm->m_flags[i] |= VMANIP_FLAG_CAVE;
590 inline bool CavesRandomWalk::isPosAboveSurface(v3s16 p)
592 if (heightmap != NULL &&
593 p.Z >= node_min.Z && p.Z <= node_max.Z &&
594 p.X >= node_min.X && p.X <= node_max.X) {
595 u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X);
596 if (heightmap[index] < p.Y)
598 } else if (p.Y > water_level) {
610 CavesV6::CavesV6(const NodeDefManager *ndef, GenerateNotifier *gennotify,
611 int water_level, content_t water_source, content_t lava_source)
616 this->gennotify = gennotify;
617 this->water_level = water_level;
619 c_water_source = water_source;
620 if (c_water_source == CONTENT_IGNORE)
621 c_water_source = ndef->getId("mapgen_water_source");
622 if (c_water_source == CONTENT_IGNORE)
623 c_water_source = CONTENT_AIR;
625 c_lava_source = lava_source;
626 if (c_lava_source == CONTENT_IGNORE)
627 c_lava_source = ndef->getId("mapgen_lava_source");
628 if (c_lava_source == CONTENT_IGNORE)
629 c_lava_source = CONTENT_AIR;
633 void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax,
634 PseudoRandom *ps, PseudoRandom *ps2,
635 bool is_large_cave, int max_stone_height, s16 *heightmap)
644 this->node_min = nmin;
645 this->node_max = nmax;
646 this->heightmap = heightmap;
647 this->large_cave = is_large_cave;
649 this->ystride = nmax.X - nmin.X + 1;
651 // Set initial parameters from randomness
652 min_tunnel_diameter = 2;
653 max_tunnel_diameter = ps->range(2, 6);
654 int dswitchint = ps->range(1, 14);
656 part_max_length_rs = ps->range(2, 4);
657 tunnel_routepoints = ps->range(5, ps->range(15, 30));
658 min_tunnel_diameter = 5;
659 max_tunnel_diameter = ps->range(7, ps->range(8, 24));
661 part_max_length_rs = ps->range(2, 9);
662 tunnel_routepoints = ps->range(10, ps->range(15, 30));
664 large_cave_is_flat = (ps->range(0, 1) == 0);
666 main_direction = v3f(0, 0, 0);
668 // Allowed route area size in nodes
669 ar = node_max - node_min + v3s16(1, 1, 1);
670 // Area starting point in nodes
674 //(this should be more than the maximum radius of the tunnel)
675 const s16 max_spread_amount = MAP_BLOCKSIZE;
676 const s16 insure = 10;
677 s16 more = MYMAX(max_spread_amount - max_tunnel_diameter / 2 - insure, 1);
678 ar += v3s16(1, 0, 1) * more * 2;
679 of -= v3s16(1, 0, 1) * more;
682 // Allow half a diameter + 7 over stone surface
683 route_y_max = -of.Y + max_stone_height + max_tunnel_diameter / 2 + 7;
685 // Limit maximum to area
686 route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
690 if (node_min.Y < water_level && node_max.Y > water_level) {
691 minpos = water_level - max_tunnel_diameter / 3 - of.Y;
692 route_y_max = water_level + max_tunnel_diameter / 3 - of.Y;
694 route_y_min = ps->range(minpos, minpos + max_tunnel_diameter);
695 route_y_min = rangelim(route_y_min, 0, route_y_max);
698 s16 route_start_y_min = route_y_min;
699 s16 route_start_y_max = route_y_max;
701 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
702 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
704 // Randomize starting position
705 orp.Z = (float)(ps->next() % ar.Z) + 0.5f;
706 orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f;
707 orp.X = (float)(ps->next() % ar.X) + 0.5f;
709 // Add generation notify begin event
710 if (gennotify != NULL) {
711 v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
712 GenNotifyType notifytype = large_cave ?
713 GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
714 gennotify->addEvent(notifytype, abs_pos);
717 // Generate some tunnel starting from orp
718 for (u16 j = 0; j < tunnel_routepoints; j++)
719 makeTunnel(j % dswitchint == 0);
721 // Add generation notify end event
722 if (gennotify != NULL) {
723 v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
724 GenNotifyType notifytype = large_cave ?
725 GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
726 gennotify->addEvent(notifytype, abs_pos);
731 void CavesV6::makeTunnel(bool dirswitch)
733 if (dirswitch && !large_cave) {
734 main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10;
735 main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30;
736 main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10;
738 main_direction *= (float)ps->range(0, 10) / 10;
742 s16 min_d = min_tunnel_diameter;
743 s16 max_d = max_tunnel_diameter;
744 rs = ps->range(min_d, max_d);
745 s16 rs_part_max_length_rs = rs * part_max_length_rs;
750 rs_part_max_length_rs,
751 rs_part_max_length_rs / 2,
752 rs_part_max_length_rs
756 rs_part_max_length_rs,
757 ps->range(1, rs_part_max_length_rs),
758 rs_part_max_length_rs
763 vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2;
764 vec.Y = (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2;
765 vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2;
767 // Jump downward sometimes
768 if (!large_cave && ps->range(0, 12) == 0) {
769 vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2;
770 vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y;
771 vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2;
774 // Do not make caves that are entirely above ground, to fix shadow bugs
775 // caused by overgenerated large caves.
776 // It is only necessary to check the startpoint and endpoint.
777 v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2;
778 v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1;
780 // If startpoint and endpoint are above ground, disable placement of nodes
781 // in carveRoute while still running all PseudoRandom calls to ensure caves
782 // are consistent with existing worlds.
783 bool tunnel_above_ground =
784 p1.Y > getSurfaceFromHeightmap(p1) &&
785 p2.Y > getSurfaceFromHeightmap(p2);
787 vec += main_direction;
792 else if (rp.X >= ar.X)
795 if (rp.Y < route_y_min)
797 else if (rp.Y >= route_y_max)
798 rp.Y = route_y_max - 1;
802 else if (rp.Z >= ar.Z)
807 float veclen = vec.getLength();
808 // As odd as it sounds, veclen is *exactly* 0.0 sometimes, causing a FPE
812 // Every second section is rough
813 bool randomize_xz = (ps2->range(1, 2) == 1);
816 for (float f = 0.f; f < 1.0f; f += 1.0f / veclen)
817 carveRoute(vec, f, randomize_xz, tunnel_above_ground);
823 void CavesV6::carveRoute(v3f vec, float f, bool randomize_xz,
824 bool tunnel_above_ground)
826 MapNode airnode(CONTENT_AIR);
827 MapNode waternode(c_water_source);
828 MapNode lavanode(c_lava_source);
830 v3s16 startp(orp.X, orp.Y, orp.Z);
833 v3f fp = orp + vec * f;
834 fp.X += 0.1f * ps->range(-10, 10);
835 fp.Z += 0.1f * ps->range(-10, 10);
836 v3s16 cp(fp.X, fp.Y, fp.Z);
841 d0 += ps->range(-1, 1);
842 d1 += ps->range(-1, 1);
845 for (s16 z0 = d0; z0 <= d1; z0++) {
846 s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
847 for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
848 if (tunnel_above_ground)
851 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
852 s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
853 for (s16 y0 = -si2; y0 <= si2; y0++) {
854 if (large_cave_is_flat) {
855 // Make large caves not so tall
856 if (rs > 7 && abs(y0) >= rs / 3)
860 v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
863 if (!vm->m_area.contains(p))
866 u32 i = vm->m_area.index(p);
867 content_t c = vm->m_data[i].getContent();
868 if (!ndef->get(c).is_ground_content)
872 int full_ymin = node_min.Y - MAP_BLOCKSIZE;
873 int full_ymax = node_max.Y + MAP_BLOCKSIZE;
875 if (full_ymin < water_level && full_ymax > water_level) {
876 vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
877 } else if (full_ymax < water_level) {
878 vm->m_data[i] = (p.Y < startp.Y - 2) ? lavanode : airnode;
880 vm->m_data[i] = airnode;
883 if (c == CONTENT_AIR)
886 vm->m_data[i] = airnode;
887 vm->m_flags[i] |= VMANIP_FLAG_CAVE;
895 inline s16 CavesV6::getSurfaceFromHeightmap(v3s16 p)
897 if (heightmap != NULL &&
898 p.Z >= node_min.Z && p.Z <= node_max.Z &&
899 p.X >= node_min.X && p.X <= node_max.X) {
900 u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X);
901 return heightmap[index];