X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fcavegen.cpp;h=e666345177209c0c484e5be89acbb92bcf81fc70;hb=e6e5fa3bf8853f149fdef16b39af3981097176a4;hp=5728c1337947beb1b1142f496f843991a18eae03;hpb=b1eb757e60c155707b8fc1b2497b632246819c7b;p=dragonfireclient.git diff --git a/src/cavegen.cpp b/src/cavegen.cpp index 5728c1337..e66634517 100644 --- a/src/cavegen.cpp +++ b/src/cavegen.cpp @@ -23,9 +23,247 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapgen_v5.h" #include "mapgen_v6.h" #include "mapgen_v7.h" +#include "mg_biome.h" #include "cavegen.h" -NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0); +static NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0); + + +//// +//// CavesNoiseIntersection +//// + +CavesNoiseIntersection::CavesNoiseIntersection( + INodeDefManager *nodedef, BiomeManager *biomemgr, v3s16 chunksize, + NoiseParams *np_cave1, NoiseParams *np_cave2, s32 seed, float cave_width) +{ + assert(nodedef); + assert(biomemgr); + + m_ndef = nodedef; + m_bmgr = biomemgr; + + m_csize = chunksize; + m_cave_width = cave_width; + + m_ystride = m_csize.X; + m_zstride_1d = m_csize.X * (m_csize.Y + 1); + + // Noises are created using 1-down overgeneration + // A Nx-by-1-by-Nz-sized plane is at the bottom of the desired for + // re-carving the solid overtop placed for blocking sunlight + noise_cave1 = new Noise(np_cave1, seed, m_csize.X, m_csize.Y + 1, m_csize.Z); + noise_cave2 = new Noise(np_cave2, seed, m_csize.X, m_csize.Y + 1, m_csize.Z); +} + + +CavesNoiseIntersection::~CavesNoiseIntersection() +{ + delete noise_cave1; + delete noise_cave2; +} + + +void CavesNoiseIntersection::generateCaves(MMVManip *vm, + v3s16 nmin, v3s16 nmax, u8 *biomemap) +{ + assert(vm); + assert(biomemap); + + noise_cave1->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z); + noise_cave2->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z); + + const v3s16 &em = vm->m_area.getExtent(); + u32 index2d = 0; // Biomemap index + + for (s16 z = nmin.Z; z <= nmax.Z; z++) + for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) { + bool column_is_open = false; // Is column open to overground + bool is_under_river = false; // Is column under river water + bool is_under_tunnel = false; // Is tunnel or is under tunnel + bool is_top_filler_above = false; // Is top or filler above node + // Indexes at column top + u32 vi = vm->m_area.index(x, nmax.Y, z); + u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride + + (x - nmin.X); // 3D noise index + // Biome of column + Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index2d]); + u16 depth_top = biome->depth_top; + u16 base_filler = depth_top + biome->depth_filler; + u16 depth_riverbed = biome->depth_riverbed; + u16 nplaced = 0; + // Don't excavate the overgenerated stone at nmax.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 = nmax.Y; y >= nmin.Y - 1; y--, + index3d -= m_ystride, + vm->m_area.add_y(em, vi, -1)) { + content_t c = vm->m_data[vi].getContent(); + + if (c == CONTENT_AIR || c == biome->c_water_top || + c == biome->c_water) { + column_is_open = true; + is_top_filler_above = false; + continue; + } + + if (c == biome->c_river_water) { + column_is_open = true; + is_under_river = true; + is_top_filler_above = false; + continue; + } + + // Ground + float d1 = contour(noise_cave1->result[index3d]); + float d2 = contour(noise_cave2->result[index3d]); + + if (d1 * d2 > m_cave_width && m_ndef->get(c).is_ground_content) { + // In tunnel and ground content, excavate + vm->m_data[vi] = MapNode(CONTENT_AIR); + is_under_tunnel = true; + // If tunnel roof is top or filler, replace with stone + if (is_top_filler_above) + vm->m_data[vi + em.X] = MapNode(biome->c_stone); + is_top_filler_above = false; + } else if (column_is_open && is_under_tunnel && + (c == biome->c_stone || c == biome->c_filler)) { + // Tunnel entrance floor, place biome surface nodes + if (is_under_river) { + if (nplaced < depth_riverbed) { + vm->m_data[vi] = MapNode(biome->c_riverbed); + is_top_filler_above = true; + nplaced++; + } else { + // Disable top/filler placement + column_is_open = false; + is_under_river = false; + is_under_tunnel = false; + } + } else if (nplaced < depth_top) { + vm->m_data[vi] = MapNode(biome->c_top); + is_top_filler_above = true; + nplaced++; + } else if (nplaced < base_filler) { + vm->m_data[vi] = MapNode(biome->c_filler); + is_top_filler_above = true; + nplaced++; + } else { + // Disable top/filler placement + column_is_open = false; + is_under_tunnel = false; + } + } else { + // Not tunnel or tunnel entrance floor + // Check node for possible replacing with stone for tunnel roof + if (c == biome->c_top || c == biome->c_filler) + is_top_filler_above = true; + + column_is_open = false; + } + } + } +} + + +//// +//// CavernsNoise +//// + +CavernsNoise::CavernsNoise( + INodeDefManager *nodedef, v3s16 chunksize, NoiseParams *np_cavern, + s32 seed, float cavern_limit, float cavern_taper, float cavern_threshold) +{ + assert(nodedef); + + m_ndef = nodedef; + + m_csize = chunksize; + m_cavern_limit = cavern_limit; + m_cavern_taper = cavern_taper; + m_cavern_threshold = cavern_threshold; + + m_ystride = m_csize.X; + m_zstride_1d = m_csize.X * (m_csize.Y + 1); + + // Noise is created using 1-down overgeneration + // A Nx-by-1-by-Nz-sized plane is at the bottom of the desired for + // re-carving the solid overtop placed for blocking sunlight + noise_cavern = new Noise(np_cavern, seed, m_csize.X, m_csize.Y + 1, m_csize.Z); + + c_water_source = m_ndef->getId("mapgen_water_source"); + if (c_water_source == CONTENT_IGNORE) + c_water_source = CONTENT_AIR; + + c_lava_source = m_ndef->getId("mapgen_lava_source"); + if (c_lava_source == CONTENT_IGNORE) + c_lava_source = CONTENT_AIR; +} + + +CavernsNoise::~CavernsNoise() +{ + delete noise_cavern; +} + + +bool CavernsNoise::generateCaverns(MMVManip *vm, v3s16 nmin, v3s16 nmax) +{ + assert(vm); + + // Calculate noise + noise_cavern->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z); + + // Cache cavern_amp values + float *cavern_amp = new float[m_csize.Y + 1]; + u8 cavern_amp_index = 0; // Index zero at column top + for (s16 y = nmax.Y; y >= nmin.Y - 1; y--, cavern_amp_index++) { + cavern_amp[cavern_amp_index] = + MYMIN((m_cavern_limit - y) / (float)m_cavern_taper, 1.0f); + } + + //// Place nodes + bool near_cavern = false; + const v3s16 &em = vm->m_area.getExtent(); + u32 index2d = 0; + + for (s16 z = nmin.Z; z <= nmax.Z; z++) + for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) { + // Reset cave_amp index to column top + cavern_amp_index = 0; + // Initial voxelmanip index at column top + u32 vi = vm->m_area.index(x, nmax.Y, z); + // Initial 3D noise index at column top + u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride + + (x - nmin.X); + // Don't excavate the overgenerated stone at node_max.Y + 1, + // this creates a 'roof' over the cavern, preventing light in + // caverns at mapchunk borders when generating mapchunks upwards. + // This 'roof' is excavated when the mapchunk above is generated. + for (s16 y = nmax.Y; y >= nmin.Y - 1; y--, + index3d -= m_ystride, + vm->m_area.add_y(em, vi, -1), + cavern_amp_index++) { + content_t c = vm->m_data[vi].getContent(); + float n_absamp_cavern = fabs(noise_cavern->result[index3d]) * + cavern_amp[cavern_amp_index]; + // Disable CavesRandomWalk at a safe distance from caverns + // to avoid excessively spreading liquids in caverns. + if (n_absamp_cavern > m_cavern_threshold - 0.1f) { + near_cavern = true; + if (n_absamp_cavern > m_cavern_threshold && + m_ndef->get(c).is_ground_content) + vm->m_data[vi] = MapNode(CONTENT_AIR); + } + } + } + + delete[] cavern_amp; + + return near_cavern; +} + //// //// CavesRandomWalk @@ -34,10 +272,11 @@ NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0 CavesRandomWalk::CavesRandomWalk( INodeDefManager *ndef, GenerateNotifier *gennotify, - int seed, + s32 seed, int water_level, content_t water_source, - content_t lava_source) + content_t lava_source, + int lava_depth) { assert(ndef); @@ -46,7 +285,7 @@ CavesRandomWalk::CavesRandomWalk( this->seed = seed; this->water_level = water_level; this->np_caveliquids = &nparams_caveliquids; - this->lava_depth = DEFAULT_LAVA_DEPTH; + this->lava_depth = lava_depth; c_water_source = water_source; if (c_water_source == CONTENT_IGNORE) @@ -78,8 +317,8 @@ void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, this->ystride = nmax.X - nmin.X + 1; // Set initial parameters from randomness - dswitchint = ps->range(1, 14); - flooded = ps->range(1, 2) == 2; + int dswitchint = ps->range(1, 14); + flooded = ps->range(1, 2) == 2; if (large_cave) { part_max_length_rs = ps->range(2, 4); @@ -104,10 +343,10 @@ void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, // Allow a bit more //(this should be more than the maximum radius of the tunnel) - s16 insure = 10; + const s16 insure = 10; s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1); - ar += v3s16(1,0,1) * more * 2; - of -= v3s16(1,0,1) * more; + ar += v3s16(1, 0, 1) * more * 2; + of -= v3s16(1, 0, 1) * more; route_y_min = 0; // Allow half a diameter + 7 over stone surface @@ -133,9 +372,9 @@ void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1); // Randomize starting position - orp.Z = (float)(ps->next() % ar.Z) + 0.5; - orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5; - orp.X = (float)(ps->next() % ar.X) + 0.5; + orp.Z = (float)(ps->next() % ar.Z) + 0.5f; + orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f; + orp.X = (float)(ps->next() % ar.X) + 0.5f; // Add generation notify begin event if (gennotify) { @@ -230,14 +469,14 @@ void CavesRandomWalk::makeTunnel(bool dirswitch) vec = rp - orp; float veclen = vec.getLength(); - if (veclen < 0.05) - veclen = 1.0; + if (veclen < 0.05f) + veclen = 1.0f; // Every second section is rough bool randomize_xz = (ps->range(1, 2) == 1); // Carve routes - for (float f = 0; f < 1.0; f += 1.0 / veclen) + for (float f = 0.f; f < 1.0f; f += 1.0f / veclen) carveRoute(vec, f, randomize_xz); orp = rp; @@ -255,15 +494,15 @@ void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz) float nval = NoisePerlin3D(np_caveliquids, startp.X, startp.Y, startp.Z, seed); - MapNode liquidnode = (nval < 0.40 && node_max.Y < lava_depth) ? + MapNode liquidnode = (nval < 0.40f && node_max.Y < lava_depth) ? lavanode : waternode; v3f fp = orp + vec * f; - fp.X += 0.1 * ps->range(-10, 10); - fp.Z += 0.1 * ps->range(-10, 10); + fp.X += 0.1f * ps->range(-10, 10); + fp.Z += 0.1f * ps->range(-10, 10); v3s16 cp(fp.X, fp.Y, fp.Z); - s16 d0 = -rs/2; + s16 d0 = -rs / 2; s16 d1 = d0 + rs; if (randomize_xz) { d0 += ps->range(-1, 1); @@ -293,7 +532,7 @@ void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz) v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0); p += of; - if (vm->m_area.contains(p) == false) + if (!vm->m_area.contains(p)) continue; u32 i = vm->m_area.index(p); @@ -388,7 +627,7 @@ void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, // Set initial parameters from randomness min_tunnel_diameter = 2; max_tunnel_diameter = ps->range(2, 6); - dswitchint = ps->range(1, 14); + int dswitchint = ps->range(1, 14); if (large_cave) { part_max_length_rs = ps->range(2, 4); tunnel_routepoints = ps->range(5, ps->range(15, 30)); @@ -410,7 +649,7 @@ void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, // Allow a bit more //(this should be more than the maximum radius of the tunnel) const s16 max_spread_amount = MAP_BLOCKSIZE; - s16 insure = 10; + const s16 insure = 10; s16 more = MYMAX(max_spread_amount - max_tunnel_diameter / 2 - insure, 1); ar += v3s16(1, 0, 1) * more * 2; of -= v3s16(1, 0, 1) * more; @@ -439,9 +678,9 @@ void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1); // Randomize starting position - orp.Z = (float)(ps->next() % ar.Z) + 0.5; - orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5; - orp.X = (float)(ps->next() % ar.X) + 0.5; + orp.Z = (float)(ps->next() % ar.Z) + 0.5f; + orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f; + orp.X = (float)(ps->next() % ar.X) + 0.5f; // Add generation notify begin event if (gennotify != NULL) { @@ -543,14 +782,14 @@ void CavesV6::makeTunnel(bool dirswitch) float veclen = vec.getLength(); // As odd as it sounds, veclen is *exactly* 0.0 sometimes, causing a FPE - if (veclen < 0.05) - veclen = 1.0; + if (veclen < 0.05f) + veclen = 1.0f; // Every second section is rough bool randomize_xz = (ps2->range(1, 2) == 1); // Carve routes - for (float f = 0; f < 1.0; f += 1.0 / veclen) + for (float f = 0.f; f < 1.0f; f += 1.0f / veclen) carveRoute(vec, f, randomize_xz, tunnel_above_ground); orp = rp; @@ -568,8 +807,8 @@ void CavesV6::carveRoute(v3f vec, float f, bool randomize_xz, startp += of; v3f fp = orp + vec * f; - fp.X += 0.1 * ps->range(-10, 10); - fp.Z += 0.1 * ps->range(-10, 10); + fp.X += 0.1f * ps->range(-10, 10); + fp.Z += 0.1f * ps->range(-10, 10); v3s16 cp(fp.X, fp.Y, fp.Z); s16 d0 = -rs / 2; @@ -597,7 +836,7 @@ void CavesV6::carveRoute(v3f vec, float f, bool randomize_xz, v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0); p += of; - if (vm->m_area.contains(p) == false) + if (!vm->m_area.contains(p)) continue; u32 i = vm->m_area.index(p); @@ -636,7 +875,8 @@ inline s16 CavesV6::getSurfaceFromHeightmap(v3s16 p) p.X >= node_min.X && p.X <= node_max.X) { u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X); return heightmap[index]; - } else { - return water_level; } + + return water_level; + }