diff options
Diffstat (limited to 'src/mapgen_v7.cpp')
-rw-r--r-- | src/mapgen_v7.cpp | 400 |
1 files changed, 223 insertions, 177 deletions
diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index 9f612de81..9fb65f577 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -1,6 +1,7 @@ /* Minetest -Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net> +Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net> +Copyright (C) 2010-2015 paramat, Matt Gregory This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -27,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_sao.h" #include "nodedef.h" #include "voxelalgorithms.h" +//#include "profiler.h" // For TimeTaker #include "settings.h" // For g_settings #include "emerge.h" #include "dungeongen.h" @@ -44,6 +46,7 @@ FlagDesc flagdesc_mapgen_v7[] = { {NULL, 0} }; + /////////////////////////////////////////////////////////////////////////////// @@ -56,7 +59,10 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge) //// amount of elements to skip for the next index //// for noise/height/biome maps (not vmanip) this->ystride = csize.X; - this->zstride = csize.X * (csize.Y + 2); + // 1-up 1-down overgeneration + this->zstride_1u1d = csize.X * (csize.Y + 2); + // 1-down overgeneration + this->zstride_1d = csize.X * (csize.Y + 1); this->biomemap = new u8[csize.X * csize.Z]; this->heightmap = new s16[csize.X * csize.Z]; @@ -65,7 +71,9 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge) this->ridge_heightmap = new s16[csize.X * csize.Z]; MapgenV7Params *sp = (MapgenV7Params *)params->sparams; - this->spflags = sp->spflags; + + this->spflags = sp->spflags; + this->cave_width = sp->cave_width; //// Terrain noise noise_terrain_base = new Noise(&sp->np_terrain_base, seed, csize.X, csize.Z); @@ -77,10 +85,12 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge) noise_ridge_uwater = new Noise(&sp->np_ridge_uwater, seed, csize.X, csize.Z); //// 3d terrain noise + // 1-up 1-down overgeneration noise_mountain = new Noise(&sp->np_mountain, seed, csize.X, csize.Y + 2, csize.Z); noise_ridge = new Noise(&sp->np_ridge, seed, csize.X, csize.Y + 2, csize.Z); - noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 2, csize.Z); - noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 2, csize.Z); + // 1-down overgeneraion + noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z); + noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z); //// Biome noise noise_heat = new Noise(¶ms->np_biome_heat, seed, csize.X, csize.Z); @@ -144,25 +154,27 @@ MapgenV7::~MapgenV7() MapgenV7Params::MapgenV7Params() { - spflags = MGV7_MOUNTAINS | MGV7_RIDGES; + spflags = MGV7_MOUNTAINS | MGV7_RIDGES; + cave_width = 0.3; np_terrain_base = NoiseParams(4, 70, v3f(600, 600, 600), 82341, 5, 0.6, 2.0); np_terrain_alt = NoiseParams(4, 25, v3f(600, 600, 600), 5934, 5, 0.6, 2.0); np_terrain_persist = NoiseParams(0.6, 0.1, v3f(2000, 2000, 2000), 539, 3, 0.6, 2.0); - np_height_select = NoiseParams(-12, 24, v3f(500, 500, 500), 4213, 6, 0.7, 2.0); + np_height_select = NoiseParams(-8, 16, v3f(500, 500, 500), 4213, 6, 0.7, 2.0); np_filler_depth = NoiseParams(0, 1.2, v3f(150, 150, 150), 261, 3, 0.7, 2.0); np_mount_height = NoiseParams(256, 112, v3f(1000, 1000, 1000), 72449, 3, 0.6, 2.0); np_ridge_uwater = NoiseParams(0, 1, v3f(1000, 1000, 1000), 85039, 5, 0.6, 2.0); np_mountain = NoiseParams(-0.6, 1, v3f(250, 350, 250), 5333, 5, 0.63, 2.0); np_ridge = NoiseParams(0, 1, v3f(100, 100, 100), 6467, 4, 0.75, 2.0); - np_cave1 = NoiseParams(0, 12, v3f(100, 100, 100), 52534, 4, 0.5, 2.0); - np_cave2 = NoiseParams(0, 12, v3f(100, 100, 100), 10325, 4, 0.5, 2.0); + np_cave1 = NoiseParams(0, 12, v3f(96, 96, 96), 52534, 4, 0.5, 2.0); + np_cave2 = NoiseParams(0, 12, v3f(96, 96, 96), 10325, 4, 0.5, 2.0); } void MapgenV7Params::readParams(const Settings *settings) { - settings->getFlagStrNoEx("mgv7_spflags", spflags, flagdesc_mapgen_v7); + settings->getFlagStrNoEx("mgv7_spflags", spflags, flagdesc_mapgen_v7); + settings->getFloatNoEx("mgv7_cave_width", cave_width); settings->getNoiseParams("mgv7_np_terrain_base", np_terrain_base); settings->getNoiseParams("mgv7_np_terrain_alt", np_terrain_alt); @@ -180,7 +192,8 @@ void MapgenV7Params::readParams(const Settings *settings) void MapgenV7Params::writeParams(Settings *settings) const { - settings->setFlagStr("mgv7_spflags", spflags, flagdesc_mapgen_v7, (u32)-1); + settings->setFlagStr("mgv7_spflags", spflags, flagdesc_mapgen_v7, U32_MAX); + settings->setFloat("mgv7_cave_width", cave_width); settings->setNoiseParams("mgv7_np_terrain_base", np_terrain_base); settings->setNoiseParams("mgv7_np_terrain_alt", np_terrain_alt); @@ -196,10 +209,10 @@ void MapgenV7Params::writeParams(Settings *settings) const } -/////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// -int MapgenV7::getGroundLevelAtPoint(v2s16 p) +int MapgenV7::getSpawnLevelAtPoint(v2s16 p) { // Base terrain calculation s16 y = baseTerrainLevelAtPoint(p.X, p.Y); @@ -207,22 +220,24 @@ int MapgenV7::getGroundLevelAtPoint(v2s16 p) // Ridge/river terrain calculation float width = 0.2; float uwatern = NoisePerlin2D(&noise_ridge_uwater->np, p.X, p.Y, seed) * 2; - // actually computing the depth of the ridge is much more expensive; - // if inside a river, simply guess + // if inside a river this is an unsuitable spawn point if (fabs(uwatern) <= width) - return water_level - 10; + return MAX_MAP_GENERATION_LIMIT; // Mountain terrain calculation - int iters = 128; // don't even bother iterating more than 128 times.. + int iters = 128; while (iters--) { - //current point would have been air - if (!getMountainTerrainAtPoint(p.X, y, p.Y)) - return y; - + if (!getMountainTerrainAtPoint(p.X, y + 1, p.Y)) { // Air, y is ground level + if (y <= water_level || y > water_level + 16) + return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point + else + return y; + } y++; } - return y; + // Unsuitable spawn point, no ground surface found + return MAX_MAP_GENERATION_LIMIT; } @@ -255,10 +270,13 @@ void MapgenV7::makeChunk(BlockMakeData *data) // Make some noise calculateNoise(); - // Generate base terrain, mountains, and ridges with initial heightmaps + // Generate terrain and ridges with initial heightmaps s16 stone_surface_max_y = generateTerrain(); - // Create heightmap + if (spflags & MGV7_RIDGES) + generateRidgeTerrain(); + + // Update heightmap to include mountain terrain updateHeightmap(node_min, node_max); // Create biomemap at heightmap surface @@ -315,7 +333,8 @@ void MapgenV7::makeChunk(BlockMakeData *data) } // Generate the registered decorations - m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max); + if (flags & MG_DECORATIONS) + m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max); // Generate the registered ores m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); @@ -341,9 +360,9 @@ void MapgenV7::makeChunk(BlockMakeData *data) void MapgenV7::calculateNoise() { //TimeTaker t("calculateNoise", NULL, PRECISION_MICRO); - int x = node_min.X; - int y = node_min.Y - 1; - int z = node_min.Z; + s16 x = node_min.X; + s16 y = node_min.Y - 1; + s16 z = node_min.Z; noise_terrain_persist->perlinMap2D(x, z); float *persistmap = noise_terrain_persist->result; @@ -352,9 +371,9 @@ void MapgenV7::calculateNoise() noise_terrain_alt->perlinMap2D(x, z, persistmap); noise_height_select->perlinMap2D(x, z); - if (flags & MG_CAVES) { - noise_cave1->perlinMap3D(x, y, z); - noise_cave2->perlinMap3D(x, y, z); + if (spflags & MGV7_MOUNTAINS) { + noise_mountain->perlinMap3D(x, y, z); + noise_mount_height->perlinMap2D(x, z); } if ((spflags & MGV7_RIDGES) && node_max.Y >= water_level) { @@ -362,7 +381,8 @@ void MapgenV7::calculateNoise() noise_ridge_uwater->perlinMap2D(x, z); } - // Mountain noises are calculated in generateMountainTerrain() + // Cave noises are calculated in generateCaves() + // only if solid terrain is present in mapchunk noise_filler_depth->perlinMap2D(x, z); noise_heat->perlinMap2D(x, z); @@ -392,7 +412,7 @@ Biome *MapgenV7::getBiomeAtPoint(v3s16 p) return bmgr->getBiome(heat, humidity, groundlevel); } -//needs to be updated + float MapgenV7::baseTerrainLevelAtPoint(s16 x, s16 z) { float hselect = NoisePerlin2D(&noise_height_select->np, x, z, seed); @@ -446,142 +466,62 @@ bool MapgenV7::getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y) } -#if 0 -void MapgenV7::carveRivers() { - MapNode n_air(CONTENT_AIR), n_water_source(c_water_source); - MapNode n_stone(c_stone); - u32 index = 0; - - int river_depth = 4; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index++) { - float terrain_mod = noise_terrain_mod->result[index]; - NoiseParams *np = noise_terrain_river->np; - np.persist = noise_terrain_persist->result[index]; - float terrain_river = NoisePerlin2DNoTxfm(np, x, z, seed); - float height = terrain_river * (1 - abs(terrain_mod)) * - noise_terrain_river->np.scale; - height = log(height * height); //log(h^3) is pretty interesting for terrain - - s16 y = heightmap[index]; - if (height < 1.0 && y > river_depth && - y - river_depth >= node_min.Y && y <= node_max.Y) { - - for (s16 ry = y; ry != y - river_depth; ry--) { - u32 vi = vm->m_area.index(x, ry, z); - vm->m_data[vi] = n_air; - } - - u32 vi = vm->m_area.index(x, y - river_depth, z); - vm->m_data[vi] = n_water_source; - } - } -} -#endif - - int MapgenV7::generateTerrain() { - s16 stone_surface_min_y; - s16 stone_surface_max_y; - - generateBaseTerrain(&stone_surface_min_y, &stone_surface_max_y); - - if ((spflags & MGV7_MOUNTAINS) && stone_surface_min_y < node_max.Y) - stone_surface_max_y = generateMountainTerrain(stone_surface_max_y); - - if (spflags & MGV7_RIDGES) - generateRidgeTerrain(); - - return stone_surface_max_y; -} - - -void MapgenV7::generateBaseTerrain(s16 *stone_surface_min_y, s16 *stone_surface_max_y) -{ MapNode n_air(CONTENT_AIR); MapNode n_stone(c_stone); MapNode n_water(c_water_source); v3s16 em = vm->m_area.getExtent(); - s16 surface_min_y = MAX_MAP_GENERATION_LIMIT; - s16 surface_max_y = -MAX_MAP_GENERATION_LIMIT; - u32 index = 0; + s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT; + u32 index2d = 0; + bool mountain_flag = spflags & MGV7_MOUNTAINS; for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index++) { - float surface_height = baseTerrainLevelFromMap(index); - s16 surface_y = (s16)surface_height; - - heightmap[index] = surface_y; - ridge_heightmap[index] = surface_y; + for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) { + s16 surface_y = baseTerrainLevelFromMap(index2d); + heightmap[index2d] = surface_y; // Create base terrain heightmap + ridge_heightmap[index2d] = surface_y; - if (surface_y < surface_min_y) - surface_min_y = surface_y; + if (surface_y > stone_surface_max_y) + stone_surface_max_y = surface_y; - if (surface_y > surface_max_y) - surface_max_y = surface_y; + u32 vi = vm->m_area.index(x, node_min.Y - 1, z); + u32 index3d = (z - node_min.Z) * zstride_1u1d + (x - node_min.X); - u32 i = vm->m_area.index(x, node_min.Y - 1, z); for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) { - if (vm->m_data[i].getContent() == CONTENT_IGNORE) { - if (y <= surface_y) - vm->m_data[i] = n_stone; - else if (y <= water_level) - vm->m_data[i] = n_water; - else - vm->m_data[i] = n_air; - } - vm->m_area.add_y(em, i, 1); - } - } - - *stone_surface_min_y = surface_min_y; - *stone_surface_max_y = surface_max_y; -} - - -int MapgenV7::generateMountainTerrain(s16 ymax) -{ - noise_mountain->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); - noise_mount_height->perlinMap2D(node_min.X, node_min.Z); - - MapNode n_stone(c_stone); - u32 j = 0; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) { - u32 vi = vm->m_area.index(node_min.X, y, z); - for (s16 x = node_min.X; x <= node_max.X; x++) { - int index = (z - node_min.Z) * csize.X + (x - node_min.X); - content_t c = vm->m_data[vi].getContent(); - - if (getMountainTerrainFromMap(j, index, y) - && (c == CONTENT_AIR || c == c_water_source)) { - vm->m_data[vi] = n_stone; - if (y > ymax) - ymax = y; + if (vm->m_data[vi].getContent() == CONTENT_IGNORE) { + if (y <= surface_y) { + vm->m_data[vi] = n_stone; // Base terrain + } else if (mountain_flag && + getMountainTerrainFromMap(index3d, index2d, y)) { + vm->m_data[vi] = n_stone; // Mountain terrain + if (y > stone_surface_max_y) + stone_surface_max_y = y; + } else if (y <= water_level) { + vm->m_data[vi] = n_water; + } else { + vm->m_data[vi] = n_air; + } } - - vi++; - j++; + vm->m_area.add_y(em, vi, 1); + index3d += ystride; } } - return ymax; + return stone_surface_max_y; } void MapgenV7::generateRidgeTerrain() { - if (node_max.Y < water_level) + if (node_max.Y < water_level - 16) return; MapNode n_water(c_water_source); MapNode n_air(CONTENT_AIR); u32 index = 0; - float width = 0.2; // TODO: figure out acceptable perlin noise values + float width = 0.2; for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) { @@ -589,7 +529,7 @@ void MapgenV7::generateRidgeTerrain() for (s16 x = node_min.X; x <= node_max.X; x++, index++, vi++) { int j = (z - node_min.Z) * csize.X + (x - node_min.X); - if (heightmap[j] < water_level - 16) + if (heightmap[j] < water_level - 16) // Use base terrain heightmap continue; float uwatern = noise_ridge_uwater->result[j] * 2; @@ -635,7 +575,7 @@ MgStoneType MapgenV7::generateBiomes(float *heat_map, float *humidity_map) // If there is air or water above enable top/filler placement, otherwise force // nplaced to stone level by setting a number exceeding any possible filler depth. - u16 nplaced = (air_above || water_above) ? 0 : (u16)-1; + u16 nplaced = (air_above || water_above) ? 0 : U16_MAX; for (s16 y = node_max.Y; y >= node_min.Y; y--) { content_t c = vm->m_data[vi].getContent(); @@ -672,7 +612,7 @@ MgStoneType MapgenV7::generateBiomes(float *heat_map, float *humidity_map) // This is done by aborting the cycle of top/filler placement // immediately by forcing nplaced to stone level. if (c_below == CONTENT_AIR || c_below == c_water_source) - nplaced = (u16)-1; + nplaced = U16_MAX; if (nplaced < depth_top) { vm->m_data[vi] = MapNode(biome->c_top); @@ -697,7 +637,7 @@ MgStoneType MapgenV7::generateBiomes(float *heat_map, float *humidity_map) air_above = true; water_above = false; } else { // Possible various nodes overgenerated from neighbouring mapchunks - nplaced = (u16)-1; // Disable top/filler placement + nplaced = U16_MAX; // Disable top/filler placement air_above = false; water_above = false; } @@ -760,6 +700,143 @@ void MapgenV7::dustTopNodes() } +void MapgenV7::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); + + v3s16 em = vm->m_area.getExtent(); + u32 index2d = 0; + + for (s16 z = node_min.Z; z <= node_max.Z; z++) + for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) { + bool column_is_open = false; // Is column open to overground + bool is_tunnel = false; // Is tunnel or tunnel floor + // Indexes at column top (node_max.Y) + u32 vi = vm->m_area.index(x, node_max.Y, z); + u32 index3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride + + (x - node_min.X); + // Biome of column + Biome *biome = (Biome *)bmgr->getRaw(biomemap[index2d]); + + // 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--, + index3d -= 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; + continue; + } + // Ground + float d1 = contour(noise_cave1->result[index3d]); + float d2 = contour(noise_cave2->result[index3d]); + + if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) { + // In tunnel and ground content, excavate + vm->m_data[vi] = MapNode(CONTENT_AIR); + is_tunnel = true; + } else { + // Not in tunnel or not ground content + if (is_tunnel && column_is_open && + (c == biome->c_filler || c == biome->c_stone)) + // Tunnel entrance floor + vm->m_data[vi] = MapNode(biome->c_top); + + column_is_open = false; + is_tunnel = false; + } + } + } + + if (node_min.Y >= water_level) + return; + + PseudoRandom ps(blockseed + 21343); + u32 bruises_count = ps.range(0, 2); + for (u32 i = 0; i < bruises_count; i++) { + CaveV7 cave(this, &ps); + cave.makeCave(node_min, node_max, max_stone_y); + } +} + + +/////////////////////////////////////////////////////////////// + + +#if 0 +int MapgenV7::generateMountainTerrain(s16 ymax) +{ + MapNode n_stone(c_stone); + u32 j = 0; + + for (s16 z = node_min.Z; z <= node_max.Z; z++) + for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) { + u32 vi = vm->m_area.index(node_min.X, y, z); + for (s16 x = node_min.X; x <= node_max.X; x++) { + int index = (z - node_min.Z) * csize.X + (x - node_min.X); + content_t c = vm->m_data[vi].getContent(); + + if (getMountainTerrainFromMap(j, index, y) + && (c == CONTENT_AIR || c == c_water_source)) { + vm->m_data[vi] = n_stone; + if (y > ymax) + ymax = y; + } + + vi++; + j++; + } + } + + return ymax; +} +#endif + + +#if 0 +void MapgenV7::carveRivers() { + MapNode n_air(CONTENT_AIR), n_water_source(c_water_source); + MapNode n_stone(c_stone); + u32 index = 0; + + int river_depth = 4; + + for (s16 z = node_min.Z; z <= node_max.Z; z++) + for (s16 x = node_min.X; x <= node_max.X; x++, index++) { + float terrain_mod = noise_terrain_mod->result[index]; + NoiseParams *np = noise_terrain_river->np; + np.persist = noise_terrain_persist->result[index]; + float terrain_river = NoisePerlin2DNoTxfm(np, x, z, seed); + float height = terrain_river * (1 - abs(terrain_mod)) * + noise_terrain_river->np.scale; + height = log(height * height); //log(h^3) is pretty interesting for terrain + + s16 y = heightmap[index]; + if (height < 1.0 && y > river_depth && + y - river_depth >= node_min.Y && y <= node_max.Y) { + + for (s16 ry = y; ry != y - river_depth; ry--) { + u32 vi = vm->m_area.index(x, ry, z); + vm->m_data[vi] = n_air; + } + + u32 vi = vm->m_area.index(x, y - river_depth, z); + vm->m_data[vi] = n_water_source; + } + } +} +#endif + + #if 0 void MapgenV7::addTopNodes() { @@ -854,34 +931,3 @@ void MapgenV7::addTopNodes() } } #endif - - -void MapgenV7::generateCaves(s16 max_stone_y) -{ - if (max_stone_y >= node_min.Y) { - u32 index = 0; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) { - u32 i = vm->m_area.index(node_min.X, y, z); - for (s16 x = node_min.X; x <= node_max.X; x++, i++, index++) { - float d1 = contour(noise_cave1->result[index]); - float d2 = contour(noise_cave2->result[index]); - if (d1 * d2 > 0.3) { - content_t c = vm->m_data[i].getContent(); - if (!ndef->get(c).is_ground_content || c == CONTENT_AIR) - continue; - - vm->m_data[i] = MapNode(CONTENT_AIR); - } - } - } - } - - PseudoRandom ps(blockseed + 21343); - u32 bruises_count = (ps.range(1, 4) == 1) ? ps.range(1, 2) : 0; - for (u32 i = 0; i < bruises_count; i++) { - CaveV7 cave(this, &ps); - cave.makeCave(node_min, node_max, max_stone_y); - } -} |