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 = (Biome *)bmgn->getBiomeAtPoint(cpabs);
511 if (biome->c_cave_liquid != CONTENT_IGNORE)
512 liquidnode = biome->c_cave_liquid;
515 if (liquidnode == CONTENT_IGNORE) {
516 // Fallback to classic behaviour using point 'startp'
517 float nval = NoisePerlin3D(np_caveliquids, startp.X,
518 startp.Y, startp.Z, seed);
519 liquidnode = (nval < 0.40f && node_max.Y < lava_depth) ?
520 lavanode : waternode;
526 d0 += ps->range(-1, 1);
527 d1 += ps->range(-1, 1);
530 bool flat_cave_floor = !large_cave && ps->range(0, 2) == 2;
532 for (s16 z0 = d0; z0 <= d1; z0++) {
533 s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
534 for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
535 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
537 s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
539 for (s16 y0 = -si2; y0 <= si2; y0++) {
540 // Make better floors in small caves
541 if (flat_cave_floor && y0 <= -rs / 2 && rs <= 7)
544 if (large_cave_is_flat) {
545 // Make large caves not so tall
546 if (rs > 7 && abs(y0) >= rs / 3)
550 v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
553 if (!vm->m_area.contains(p))
556 u32 i = vm->m_area.index(p);
557 content_t c = vm->m_data[i].getContent();
558 if (!ndef->get(c).is_ground_content)
562 int full_ymin = node_min.Y - MAP_BLOCKSIZE;
563 int full_ymax = node_max.Y + MAP_BLOCKSIZE;
565 if (flooded && full_ymin < water_level && full_ymax > water_level)
566 vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
567 else if (flooded && full_ymax < water_level)
568 vm->m_data[i] = (p.Y < startp.Y - 4) ? liquidnode : airnode;
570 vm->m_data[i] = airnode;
572 vm->m_data[i] = airnode;
573 vm->m_flags[i] |= VMANIP_FLAG_CAVE;
581 inline bool CavesRandomWalk::isPosAboveSurface(v3s16 p)
583 if (heightmap != NULL &&
584 p.Z >= node_min.Z && p.Z <= node_max.Z &&
585 p.X >= node_min.X && p.X <= node_max.X) {
586 u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X);
587 if (heightmap[index] < p.Y)
589 } else if (p.Y > water_level) {
601 CavesV6::CavesV6(const NodeDefManager *ndef, GenerateNotifier *gennotify,
602 int water_level, content_t water_source, content_t lava_source)
607 this->gennotify = gennotify;
608 this->water_level = water_level;
610 c_water_source = water_source;
611 if (c_water_source == CONTENT_IGNORE)
612 c_water_source = ndef->getId("mapgen_water_source");
613 if (c_water_source == CONTENT_IGNORE)
614 c_water_source = CONTENT_AIR;
616 c_lava_source = lava_source;
617 if (c_lava_source == CONTENT_IGNORE)
618 c_lava_source = ndef->getId("mapgen_lava_source");
619 if (c_lava_source == CONTENT_IGNORE)
620 c_lava_source = CONTENT_AIR;
624 void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax,
625 PseudoRandom *ps, PseudoRandom *ps2,
626 bool is_large_cave, int max_stone_height, s16 *heightmap)
635 this->node_min = nmin;
636 this->node_max = nmax;
637 this->heightmap = heightmap;
638 this->large_cave = is_large_cave;
640 this->ystride = nmax.X - nmin.X + 1;
642 // Set initial parameters from randomness
643 min_tunnel_diameter = 2;
644 max_tunnel_diameter = ps->range(2, 6);
645 int dswitchint = ps->range(1, 14);
647 part_max_length_rs = ps->range(2, 4);
648 tunnel_routepoints = ps->range(5, ps->range(15, 30));
649 min_tunnel_diameter = 5;
650 max_tunnel_diameter = ps->range(7, ps->range(8, 24));
652 part_max_length_rs = ps->range(2, 9);
653 tunnel_routepoints = ps->range(10, ps->range(15, 30));
655 large_cave_is_flat = (ps->range(0, 1) == 0);
657 main_direction = v3f(0, 0, 0);
659 // Allowed route area size in nodes
660 ar = node_max - node_min + v3s16(1, 1, 1);
661 // Area starting point in nodes
665 //(this should be more than the maximum radius of the tunnel)
666 const s16 max_spread_amount = MAP_BLOCKSIZE;
667 const s16 insure = 10;
668 s16 more = MYMAX(max_spread_amount - max_tunnel_diameter / 2 - insure, 1);
669 ar += v3s16(1, 0, 1) * more * 2;
670 of -= v3s16(1, 0, 1) * more;
673 // Allow half a diameter + 7 over stone surface
674 route_y_max = -of.Y + max_stone_height + max_tunnel_diameter / 2 + 7;
676 // Limit maximum to area
677 route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
681 if (node_min.Y < water_level && node_max.Y > water_level) {
682 minpos = water_level - max_tunnel_diameter / 3 - of.Y;
683 route_y_max = water_level + max_tunnel_diameter / 3 - of.Y;
685 route_y_min = ps->range(minpos, minpos + max_tunnel_diameter);
686 route_y_min = rangelim(route_y_min, 0, route_y_max);
689 s16 route_start_y_min = route_y_min;
690 s16 route_start_y_max = route_y_max;
692 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
693 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
695 // Randomize starting position
696 orp.Z = (float)(ps->next() % ar.Z) + 0.5f;
697 orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f;
698 orp.X = (float)(ps->next() % ar.X) + 0.5f;
700 // Add generation notify begin event
701 if (gennotify != NULL) {
702 v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
703 GenNotifyType notifytype = large_cave ?
704 GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
705 gennotify->addEvent(notifytype, abs_pos);
708 // Generate some tunnel starting from orp
709 for (u16 j = 0; j < tunnel_routepoints; j++)
710 makeTunnel(j % dswitchint == 0);
712 // Add generation notify end event
713 if (gennotify != NULL) {
714 v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
715 GenNotifyType notifytype = large_cave ?
716 GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
717 gennotify->addEvent(notifytype, abs_pos);
722 void CavesV6::makeTunnel(bool dirswitch)
724 if (dirswitch && !large_cave) {
725 main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10;
726 main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30;
727 main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10;
729 main_direction *= (float)ps->range(0, 10) / 10;
733 s16 min_d = min_tunnel_diameter;
734 s16 max_d = max_tunnel_diameter;
735 rs = ps->range(min_d, max_d);
736 s16 rs_part_max_length_rs = rs * part_max_length_rs;
741 rs_part_max_length_rs,
742 rs_part_max_length_rs / 2,
743 rs_part_max_length_rs
747 rs_part_max_length_rs,
748 ps->range(1, rs_part_max_length_rs),
749 rs_part_max_length_rs
754 vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2;
755 vec.Y = (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2;
756 vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2;
758 // Jump downward sometimes
759 if (!large_cave && ps->range(0, 12) == 0) {
760 vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2;
761 vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y;
762 vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2;
765 // Do not make caves that are entirely above ground, to fix shadow bugs
766 // caused by overgenerated large caves.
767 // It is only necessary to check the startpoint and endpoint.
768 v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2;
769 v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1;
771 // If startpoint and endpoint are above ground, disable placement of nodes
772 // in carveRoute while still running all PseudoRandom calls to ensure caves
773 // are consistent with existing worlds.
774 bool tunnel_above_ground =
775 p1.Y > getSurfaceFromHeightmap(p1) &&
776 p2.Y > getSurfaceFromHeightmap(p2);
778 vec += main_direction;
783 else if (rp.X >= ar.X)
786 if (rp.Y < route_y_min)
788 else if (rp.Y >= route_y_max)
789 rp.Y = route_y_max - 1;
793 else if (rp.Z >= ar.Z)
798 float veclen = vec.getLength();
799 // As odd as it sounds, veclen is *exactly* 0.0 sometimes, causing a FPE
803 // Every second section is rough
804 bool randomize_xz = (ps2->range(1, 2) == 1);
807 for (float f = 0.f; f < 1.0f; f += 1.0f / veclen)
808 carveRoute(vec, f, randomize_xz, tunnel_above_ground);
814 void CavesV6::carveRoute(v3f vec, float f, bool randomize_xz,
815 bool tunnel_above_ground)
817 MapNode airnode(CONTENT_AIR);
818 MapNode waternode(c_water_source);
819 MapNode lavanode(c_lava_source);
821 v3s16 startp(orp.X, orp.Y, orp.Z);
824 v3f fp = orp + vec * f;
825 fp.X += 0.1f * ps->range(-10, 10);
826 fp.Z += 0.1f * ps->range(-10, 10);
827 v3s16 cp(fp.X, fp.Y, fp.Z);
832 d0 += ps->range(-1, 1);
833 d1 += ps->range(-1, 1);
836 for (s16 z0 = d0; z0 <= d1; z0++) {
837 s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
838 for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
839 if (tunnel_above_ground)
842 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
843 s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
844 for (s16 y0 = -si2; y0 <= si2; y0++) {
845 if (large_cave_is_flat) {
846 // Make large caves not so tall
847 if (rs > 7 && abs(y0) >= rs / 3)
851 v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
854 if (!vm->m_area.contains(p))
857 u32 i = vm->m_area.index(p);
858 content_t c = vm->m_data[i].getContent();
859 if (!ndef->get(c).is_ground_content)
863 int full_ymin = node_min.Y - MAP_BLOCKSIZE;
864 int full_ymax = node_max.Y + MAP_BLOCKSIZE;
866 if (full_ymin < water_level && full_ymax > water_level) {
867 vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
868 } else if (full_ymax < water_level) {
869 vm->m_data[i] = (p.Y < startp.Y - 2) ? lavanode : airnode;
871 vm->m_data[i] = airnode;
874 if (c == CONTENT_AIR)
877 vm->m_data[i] = airnode;
878 vm->m_flags[i] |= VMANIP_FLAG_CAVE;
886 inline s16 CavesV6::getSurfaceFromHeightmap(v3s16 p)
888 if (heightmap != NULL &&
889 p.Z >= node_min.Z && p.Z <= node_max.Z &&
890 p.X >= node_min.X && p.X <= node_max.X) {
891 u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X);
892 return heightmap[index];