X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fmapgen.cpp;h=1a31a8bcb0d728f35c74266a12aaf6490bbf1476;hb=86a0f5603b9e936b8527ae22c4aeaf5714c9d279;hp=b7711b01cdfbcad6b4a7ab61b0e9ef7f8c4eb29e;hpb=8ec3fc35c656544a55f7f8ece9359c9e2b472e8f;p=oweals%2Fminetest.git diff --git a/src/mapgen.cpp b/src/mapgen.cpp index b7711b01c..1a31a8bcb 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -35,15 +35,18 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "treegen.h" #include "mapgen_v6.h" #include "mapgen_v7.h" +#include "serialization.h" +#include "util/serialize.h" +#include "filesys.h" + FlagDesc flagdesc_mapgen[] = { - {"trees", MG_TREES}, - {"caves", MG_CAVES}, - {"dungeons", MG_DUNGEONS}, - {"v6_jungles", MGV6_JUNGLES}, - {"v6_biome_blend", MGV6_BIOME_BLEND}, - {"flat", MG_FLAT}, - {NULL, 0} + {"trees", MG_TREES}, + {"caves", MG_CAVES}, + {"dungeons", MG_DUNGEONS}, + {"flat", MG_FLAT}, + {"light", MG_LIGHT}, + {NULL, 0} }; FlagDesc flagdesc_ore[] = { @@ -53,6 +56,22 @@ FlagDesc flagdesc_ore[] = { {NULL, 0} }; +FlagDesc flagdesc_deco_schematic[] = { + {"place_center_x", DECO_PLACE_CENTER_X}, + {"place_center_y", DECO_PLACE_CENTER_Y}, + {"place_center_z", DECO_PLACE_CENTER_Z}, + {NULL, 0} +}; + +FlagDesc flagdesc_gennotify[] = { + {"dungeon", 1 << GENNOTIFY_DUNGEON}, + {"temple", 1 << GENNOTIFY_TEMPLE}, + {"cave_begin", 1 << GENNOTIFY_CAVE_BEGIN}, + {"cave_end", 1 << GENNOTIFY_CAVE_END}, + {"large_cave_begin", 1 << GENNOTIFY_LARGECAVE_BEGIN}, + {"large_cave_end", 1 << GENNOTIFY_LARGECAVE_END}, + {NULL, 0} +}; /////////////////////////////////////////////////////////////////////////////// @@ -71,30 +90,35 @@ Ore *createOre(OreType type) { } +Ore::~Ore() { + delete np; + delete noise; +} + + void Ore::resolveNodeNames(INodeDefManager *ndef) { if (ore == CONTENT_IGNORE) { ore = ndef->getId(ore_name); if (ore == CONTENT_IGNORE) { errorstream << "Ore::resolveNodeNames: ore node '" << ore_name << "' not defined"; - ore = CONTENT_AIR; - wherein = CONTENT_AIR; + ore = CONTENT_AIR; + wherein.push_back(CONTENT_AIR); + return; } } - - if (wherein == CONTENT_IGNORE) { - wherein = ndef->getId(wherein_name); - if (wherein == CONTENT_IGNORE) { - errorstream << "Ore::resolveNodeNames: wherein node '" - << wherein_name << "' not defined"; - ore = CONTENT_AIR; - wherein = CONTENT_AIR; + + for (size_t i=0; i != wherein_names.size(); i++) { + std::string name = wherein_names[i]; + content_t c = ndef->getId(name); + if (c != CONTENT_IGNORE) { + wherein.push_back(c); } } } -void OreScatter::generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { +void Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { int in_range = 0; in_range |= (nmin.Y <= height_max && nmax.Y >= height_min); @@ -103,13 +127,7 @@ void OreScatter::generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { if (!in_range) return; - resolveNodeNames(mg->ndef); - - MapNode n_ore(ore); - ManualMapVoxelManipulator *vm = mg->vm; - PseudoRandom pr(blockseed); int ymin, ymax; - if (in_range & ORE_RANGE_MIRROR) { ymin = MYMAX(nmin.Y, -height_max); ymax = MYMIN(nmax.Y, -height_min); @@ -119,7 +137,18 @@ void OreScatter::generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { } if (clust_size >= ymax - ymin + 1) return; - + + nmin.Y = ymin; + nmax.Y = ymax; + generate(mg->vm, mg->seed, blockseed, nmin, nmax); +} + + +void OreScatter::generate(ManualMapVoxelManipulator *vm, int seed, + u32 blockseed, v3s16 nmin, v3s16 nmax) { + PseudoRandom pr(blockseed); + MapNode n_ore(ore, 0, ore_param2); + int volume = (nmax.X - nmin.X + 1) * (nmax.Y - nmin.Y + 1) * (nmax.Z - nmin.Z + 1); @@ -129,77 +158,50 @@ void OreScatter::generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { for (int i = 0; i != nclusters; i++) { int x0 = pr.range(nmin.X, nmax.X - csize + 1); - int y0 = pr.range(ymin, ymax - csize + 1); + int y0 = pr.range(nmin.Y, nmax.Y - csize + 1); int z0 = pr.range(nmin.Z, nmax.Z - csize + 1); - - if (np && (NoisePerlin3D(np, x0, y0, z0, mg->seed) < nthresh)) + + if (np && (NoisePerlin3D(np, x0, y0, z0, seed) < nthresh)) continue; - + for (int z1 = 0; z1 != csize; z1++) for (int y1 = 0; y1 != csize; y1++) for (int x1 = 0; x1 != csize; x1++) { if (pr.range(1, orechance) != 1) continue; - + u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1); - if (vm->m_data[i].getContent() == wherein) - vm->m_data[i] = n_ore; + for (size_t ii = 0; ii < wherein.size(); ii++) + if (vm->m_data[i].getContent() == wherein[ii]) + vm->m_data[i] = n_ore; } } } -void OreSheet::generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { - int in_range = 0; - - in_range |= (nmin.Y <= height_max && nmax.Y >= height_min); - if (flags & OREFLAG_ABSHEIGHT) - in_range |= (nmin.Y >= -height_max && nmax.Y <= -height_min) << 1; - if (!in_range) - return; - - resolveNodeNames(mg->ndef); - - MapNode n_ore(ore); - ManualMapVoxelManipulator *vm = mg->vm; +void OreSheet::generate(ManualMapVoxelManipulator *vm, int seed, + u32 blockseed, v3s16 nmin, v3s16 nmax) { PseudoRandom pr(blockseed + 4234); - int ymin, ymax; - - if (in_range & ORE_RANGE_MIRROR) { - ymin = MYMAX(nmin.Y, -height_max); - ymax = MYMIN(nmax.Y, -height_min); - } else { - ymin = MYMAX(nmin.Y, height_min); - ymax = MYMIN(nmax.Y, height_max); - } + MapNode n_ore(ore, 0, ore_param2); - if (clust_size >= ymax - ymin + 1) - return; - - int x0 = nmin.X; - int z0 = nmin.Z; - - int x1 = nmax.X; - int z1 = nmax.Z; - int max_height = clust_size; - int y_start = pr.range(ymin, ymax - max_height); - + int y_start = pr.range(nmin.Y, nmax.Y - max_height); + if (!noise) { int sx = nmax.X - nmin.X + 1; int sz = nmax.Z - nmin.Z + 1; noise = new Noise(np, 0, sx, sz); } - noise->seed = mg->seed + y_start; - noise->perlinMap2D(x0, z0); - + noise->seed = seed + y_start; + noise->perlinMap2D(nmin.X, nmin.Z); + int index = 0; - for (int z = z0; z != z1; z++) - for (int x = x0; x != x1; x++) { + for (int z = nmin.Z; z <= nmax.Z; z++) + for (int x = nmin.X; x <= nmax.X; x++) { float noiseval = noise->result[index++]; if (noiseval < nthresh) continue; - + int height = max_height * (1. / pr.range(1, 3)); int y0 = y_start + np->scale * noiseval; //pr.range(1, 3) - 1; int y1 = y0 + height; @@ -207,11 +209,770 @@ void OreSheet::generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { u32 i = vm->m_area.index(x, y, z); if (!vm->m_area.contains(i)) continue; - - if (vm->m_data[i].getContent() == wherein) - vm->m_data[i] = n_ore; + + for (size_t ii = 0; ii < wherein.size(); ii++) + if (vm->m_data[i].getContent() == wherein[ii]) + vm->m_data[i] = n_ore; + } + } +} + + +/////////////////////////////////////////////////////////////////////////////// + + +Decoration *createDecoration(DecorationType type) { + switch (type) { + case DECO_SIMPLE: + return new DecoSimple; + case DECO_SCHEMATIC: + return new DecoSchematic; + //case DECO_LSYSTEM: + // return new DecoLSystem; + default: + return NULL; + } +} + + +Decoration::Decoration() { + mapseed = 0; + np = NULL; + fill_ratio = 0; + sidelen = 1; +} + + +Decoration::~Decoration() { + delete np; +} + + +void Decoration::resolveNodeNames(INodeDefManager *ndef) { + this->ndef = ndef; + + if (c_place_on == CONTENT_IGNORE) + c_place_on = ndef->getId(place_on_name); +} + + +void Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { + PseudoRandom ps(blockseed + 53); + int carea_size = nmax.X - nmin.X + 1; + + // Divide area into parts + if (carea_size % sidelen) { + errorstream << "Decoration::placeDeco: chunk size is not divisible by " + "sidelen; setting sidelen to " << carea_size << std::endl; + sidelen = carea_size; + } + + s16 divlen = carea_size / sidelen; + int area = sidelen * sidelen; + + for (s16 z0 = 0; z0 < divlen; z0++) + for (s16 x0 = 0; x0 < divlen; x0++) { + v2s16 p2d_center( // Center position of part of division + nmin.X + sidelen / 2 + sidelen * x0, + nmin.Z + sidelen / 2 + sidelen * z0 + ); + v2s16 p2d_min( // Minimum edge of part of division + nmin.X + sidelen * x0, + nmin.Z + sidelen * z0 + ); + v2s16 p2d_max( // Maximum edge of part of division + nmin.X + sidelen + sidelen * x0 - 1, + nmin.Z + sidelen + sidelen * z0 - 1 + ); + + // Amount of decorations + float nval = np ? + NoisePerlin2D(np, p2d_center.X, p2d_center.Y, mapseed) : + fill_ratio; + u32 deco_count = area * MYMAX(nval, 0.f); + + for (u32 i = 0; i < deco_count; i++) { + s16 x = ps.range(p2d_min.X, p2d_max.X); + s16 z = ps.range(p2d_min.Y, p2d_max.Y); + + int mapindex = carea_size * (z - nmin.Z) + (x - nmin.X); + + s16 y = mg->heightmap ? + mg->heightmap[mapindex] : + mg->findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y); + + if (y < nmin.Y || y > nmax.Y) + continue; + + int height = getHeight(); + int max_y = nmax.Y;// + MAP_BLOCKSIZE - 1; + if (y + 1 + height > max_y) { + continue; +#if 0 + printf("Decoration at (%d %d %d) cut off\n", x, y, z); + //add to queue + JMutexAutoLock cutofflock(cutoff_mutex); + cutoffs.push_back(CutoffData(x, y, z, height)); +#endif + } + + if (mg->biomemap) { + std::set::iterator iter; + + if (biomes.size()) { + iter = biomes.find(mg->biomemap[mapindex]); + if (iter == biomes.end()) + continue; + } + } + + generate(mg, &ps, max_y, v3s16(x, y, z)); + } + } +} + + +#if 0 +void Decoration::placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { + PseudoRandom pr(blockseed + 53); + std::vector handled_cutoffs; + + // Copy over the cutoffs we're interested in so we don't needlessly hold a lock + { + JMutexAutoLock cutofflock(cutoff_mutex); + for (std::list::iterator i = cutoffs.begin(); + i != cutoffs.end(); ++i) { + CutoffData cutoff = *i; + v3s16 p = cutoff.p; + s16 height = cutoff.height; + if (p.X < nmin.X || p.X > nmax.X || + p.Z < nmin.Z || p.Z > nmax.Z) + continue; + if (p.Y + height < nmin.Y || p.Y > nmax.Y) + continue; + + handled_cutoffs.push_back(cutoff); + } + } + + // Generate the cutoffs + for (size_t i = 0; i != handled_cutoffs.size(); i++) { + v3s16 p = handled_cutoffs[i].p; + s16 height = handled_cutoffs[i].height; + + if (p.Y + height > nmax.Y) { + //printf("Decoration at (%d %d %d) cut off again!\n", p.X, p.Y, p.Z); + cuttoffs.push_back(v3s16(p.X, p.Y, p.Z)); + } + + generate(mg, &pr, nmax.Y, nmin.Y - p.Y, v3s16(p.X, nmin.Y, p.Z)); + } + + // Remove cutoffs that were handled from the cutoff list + { + JMutexAutoLock cutofflock(cutoff_mutex); + for (std::list::iterator i = cutoffs.begin(); + i != cutoffs.end(); ++i) { + + for (size_t j = 0; j != handled_cutoffs.size(); j++) { + CutoffData coff = *i; + if (coff.p == handled_cutoffs[j].p) + i = cutoffs.erase(i); + } + } + } +} +#endif + + +/////////////////////////////////////////////////////////////////////////////// + + +void DecoSimple::resolveNodeNames(INodeDefManager *ndef) { + Decoration::resolveNodeNames(ndef); + + if (c_deco == CONTENT_IGNORE && !decolist_names.size()) { + c_deco = ndef->getId(deco_name); + if (c_deco == CONTENT_IGNORE) { + errorstream << "DecoSimple::resolveNodeNames: decoration node '" + << deco_name << "' not defined" << std::endl; + c_deco = CONTENT_AIR; + } + } + if (c_spawnby == CONTENT_IGNORE) { + c_spawnby = ndef->getId(spawnby_name); + if (c_spawnby == CONTENT_IGNORE) { + errorstream << "DecoSimple::resolveNodeNames: spawnby node '" + << spawnby_name << "' not defined" << std::endl; + nspawnby = -1; + c_spawnby = CONTENT_AIR; + } + } + + if (c_decolist.size()) + return; + + for (size_t i = 0; i != decolist_names.size(); i++) { + content_t c = ndef->getId(decolist_names[i]); + if (c == CONTENT_IGNORE) { + errorstream << "DecoSimple::resolveNodeNames: decolist node '" + << decolist_names[i] << "' not defined" << std::endl; + c = CONTENT_AIR; + } + c_decolist.push_back(c); + } +} + + +void DecoSimple::generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p) { + ManualMapVoxelManipulator *vm = mg->vm; + + u32 vi = vm->m_area.index(p); + if (vm->m_data[vi].getContent() != c_place_on && + c_place_on != CONTENT_IGNORE) + return; + + if (nspawnby != -1) { + int nneighs = 0; + v3s16 dirs[8] = { // a Moore neighborhood + v3s16( 0, 0, 1), + v3s16( 0, 0, -1), + v3s16( 1, 0, 0), + v3s16(-1, 0, 0), + v3s16( 1, 0, 1), + v3s16(-1, 0, 1), + v3s16(-1, 0, -1), + v3s16( 1, 0, -1) + }; + + for (int i = 0; i != 8; i++) { + u32 index = vm->m_area.index(p + dirs[i]); + if (vm->m_area.contains(index) && + vm->m_data[index].getContent() == c_spawnby) + nneighs++; + } + + if (nneighs < nspawnby) + return; + } + + size_t ndecos = c_decolist.size(); + content_t c_place = ndecos ? c_decolist[pr->range(0, ndecos - 1)] : c_deco; + + s16 height = (deco_height_max > 0) ? + pr->range(deco_height, deco_height_max) : deco_height; + + height = MYMIN(height, max_y - p.Y); + + v3s16 em = vm->m_area.getExtent(); + for (int i = 0; i < height; i++) { + vm->m_area.add_y(em, vi, 1); + + content_t c = vm->m_data[vi].getContent(); + if (c != CONTENT_AIR && c != CONTENT_IGNORE) + break; + + vm->m_data[vi] = MapNode(c_place); + } +} + + +int DecoSimple::getHeight() { + return (deco_height_max > 0) ? deco_height_max : deco_height; +} + + +std::string DecoSimple::getName() { + return deco_name; +} + + +/////////////////////////////////////////////////////////////////////////////// + + +DecoSchematic::DecoSchematic() { + node_names = NULL; + schematic = NULL; + slice_probs = NULL; + flags = 0; + size = v3s16(0, 0, 0); +} + + +DecoSchematic::~DecoSchematic() { + delete node_names; + delete []schematic; + delete []slice_probs; +} + + +void DecoSchematic::resolveNodeNames(INodeDefManager *ndef) { + Decoration::resolveNodeNames(ndef); + + if (filename.empty()) + return; + + if (!node_names) { + errorstream << "DecoSchematic::resolveNodeNames: node name list was " + "not created" << std::endl; + return; + } + + for (size_t i = 0; i != node_names->size(); i++) { + std::string name = node_names->at(i); + + std::map::iterator it; + it = replacements.find(name); + if (it != replacements.end()) + name = it->second; + + content_t c = ndef->getId(name); + if (c == CONTENT_IGNORE) { + errorstream << "DecoSchematic::resolveNodeNames: node '" + << name << "' not defined" << std::endl; + c = CONTENT_AIR; + } + + c_nodes.push_back(c); + } + + for (int i = 0; i != size.X * size.Y * size.Z; i++) + schematic[i].setContent(c_nodes[schematic[i].getContent()]); + + delete node_names; + node_names = NULL; +} + + +void DecoSchematic::generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p) { + ManualMapVoxelManipulator *vm = mg->vm; + + if (flags & DECO_PLACE_CENTER_X) + p.X -= (size.X + 1) / 2; + if (flags & DECO_PLACE_CENTER_Y) + p.Y -= (size.Y + 1) / 2; + if (flags & DECO_PLACE_CENTER_Z) + p.Z -= (size.Z + 1) / 2; + + u32 vi = vm->m_area.index(p); + if (vm->m_data[vi].getContent() != c_place_on && + c_place_on != CONTENT_IGNORE) + return; + + Rotation rot = (rotation == ROTATE_RAND) ? + (Rotation)pr->range(ROTATE_0, ROTATE_270) : rotation; + + blitToVManip(p, vm, rot, false); +} + + +int DecoSchematic::getHeight() { + return size.Y; +} + + +std::string DecoSchematic::getName() { + return filename; +} + + +void DecoSchematic::blitToVManip(v3s16 p, ManualMapVoxelManipulator *vm, + Rotation rot, bool force_placement) { + int xstride = 1; + int ystride = size.X; + int zstride = size.X * size.Y; + + s16 sx = size.X; + s16 sy = size.Y; + s16 sz = size.Z; + + int i_start, i_step_x, i_step_z; + switch (rot) { + case ROTATE_90: + i_start = sx - 1; + i_step_x = zstride; + i_step_z = -xstride; + SWAP(s16, sx, sz); + break; + case ROTATE_180: + i_start = zstride * (sz - 1) + sx - 1; + i_step_x = -xstride; + i_step_z = -zstride; + break; + case ROTATE_270: + i_start = zstride * (sz - 1); + i_step_x = -zstride; + i_step_z = xstride; + SWAP(s16, sx, sz); + break; + default: + i_start = 0; + i_step_x = xstride; + i_step_z = zstride; + } + + s16 y_map = p.Y; + for (s16 y = 0; y != sy; y++) { + if (slice_probs[y] != MTSCHEM_PROB_ALWAYS && + myrand_range(1, 255) > slice_probs[y]) + continue; + + for (s16 z = 0; z != sz; z++) { + u32 i = z * i_step_z + y * ystride + i_start; + for (s16 x = 0; x != sx; x++, i += i_step_x) { + u32 vi = vm->m_area.index(p.X + x, y_map, p.Z + z); + if (!vm->m_area.contains(vi)) + continue; + + if (schematic[i].getContent() == CONTENT_IGNORE) + continue; + + if (schematic[i].param1 == MTSCHEM_PROB_NEVER) + continue; + + if (!force_placement) { + content_t c = vm->m_data[vi].getContent(); + if (c != CONTENT_AIR && c != CONTENT_IGNORE) + continue; + } + + if (schematic[i].param1 != MTSCHEM_PROB_ALWAYS && + myrand_range(1, 255) > schematic[i].param1) + continue; + + vm->m_data[vi] = schematic[i]; + vm->m_data[vi].param1 = 0; + + if (rot) + vm->m_data[vi].rotateAlongYAxis(ndef, rot); + } + } + y_map++; + } +} + + +void DecoSchematic::placeStructure(Map *map, v3s16 p, bool force_placement) { + assert(schematic != NULL); + ManualMapVoxelManipulator *vm = new ManualMapVoxelManipulator(map); + + Rotation rot = (rotation == ROTATE_RAND) ? + (Rotation)myrand_range(ROTATE_0, ROTATE_270) : rotation; + + v3s16 s = (rot == ROTATE_90 || rot == ROTATE_270) ? + v3s16(size.Z, size.Y, size.X) : size; + + if (flags & DECO_PLACE_CENTER_X) + p.X -= (s.X + 1) / 2; + if (flags & DECO_PLACE_CENTER_Y) + p.Y -= (s.Y + 1) / 2; + if (flags & DECO_PLACE_CENTER_Z) + p.Z -= (s.Z + 1) / 2; + + v3s16 bp1 = getNodeBlockPos(p); + v3s16 bp2 = getNodeBlockPos(p + s - v3s16(1,1,1)); + vm->initialEmerge(bp1, bp2); + + blitToVManip(p, vm, rot, force_placement); + + std::map lighting_modified_blocks; + std::map modified_blocks; + vm->blitBackAll(&modified_blocks); + + // TODO: Optimize this by using Mapgen::calcLighting() instead + lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end()); + map->updateLighting(lighting_modified_blocks, modified_blocks); + + MapEditEvent event; + event.type = MEET_OTHER; + for (std::map::iterator + it = modified_blocks.begin(); + it != modified_blocks.end(); ++it) + event.modified_blocks.insert(it->first); + + map->dispatchEvent(&event); +} + + +bool DecoSchematic::loadSchematicFile() { + content_t cignore = CONTENT_IGNORE; + bool have_cignore = false; + + std::ifstream is(filename.c_str(), std::ios_base::binary); + + u32 signature = readU32(is); + if (signature != MTSCHEM_FILE_SIGNATURE) { + errorstream << "loadSchematicFile: invalid schematic " + "file" << std::endl; + return false; + } + + u16 version = readU16(is); + if (version > MTSCHEM_FILE_VER_HIGHEST_READ) { + errorstream << "loadSchematicFile: unsupported schematic " + "file version" << std::endl; + return false; + } + + size = readV3S16(is); + + delete []slice_probs; + slice_probs = new u8[size.Y]; + if (version >= 3) { + for (int y = 0; y != size.Y; y++) + slice_probs[y] = readU8(is); + } else { + for (int y = 0; y != size.Y; y++) + slice_probs[y] = MTSCHEM_PROB_ALWAYS; + } + + int nodecount = size.X * size.Y * size.Z; + + u16 nidmapcount = readU16(is); + + node_names = new std::vector; + for (int i = 0; i != nidmapcount; i++) { + std::string name = deSerializeString(is); + if (name == "ignore") { + name = "air"; + cignore = i; + have_cignore = true; + } + node_names->push_back(name); + } + + delete []schematic; + schematic = new MapNode[nodecount]; + MapNode::deSerializeBulk(is, SER_FMT_VER_HIGHEST_READ, schematic, + nodecount, 2, 2, true); + + if (version == 1) { // fix up the probability values + for (int i = 0; i != nodecount; i++) { + if (schematic[i].param1 == 0) + schematic[i].param1 = MTSCHEM_PROB_ALWAYS; + if (have_cignore && schematic[i].getContent() == cignore) + schematic[i].param1 = MTSCHEM_PROB_NEVER; + } + } + + return true; +} + + +/* + Minetest Schematic File Format + + All values are stored in big-endian byte order. + [u32] signature: 'MTSM' + [u16] version: 3 + [u16] size X + [u16] size Y + [u16] size Z + For each Y: + [u8] slice probability value + [Name-ID table] Name ID Mapping Table + [u16] name-id count + For each name-id mapping: + [u16] name length + [u8[]] name + ZLib deflated { + For each node in schematic: (for z, y, x) + [u16] content + For each node in schematic: + [u8] probability of occurance (param1) + For each node in schematic: + [u8] param2 + } + + Version changes: + 1 - Initial version + 2 - Fixed messy never/always place; 0 probability is now never, 0xFF is always + 3 - Added y-slice probabilities; this allows for variable height structures +*/ +void DecoSchematic::saveSchematicFile(INodeDefManager *ndef) { + std::ostringstream ss(std::ios_base::binary); + + writeU32(ss, MTSCHEM_FILE_SIGNATURE); // signature + writeU16(ss, MTSCHEM_FILE_VER_HIGHEST_WRITE); // version + writeV3S16(ss, size); // schematic size + + for (int y = 0; y != size.Y; y++) // Y slice probabilities + writeU8(ss, slice_probs[y]); + + std::vector usednodes; + int nodecount = size.X * size.Y * size.Z; + build_nnlist_and_update_ids(schematic, nodecount, &usednodes); + + u16 numids = usednodes.size(); + writeU16(ss, numids); // name count + for (int i = 0; i != numids; i++) + ss << serializeString(ndef->get(usednodes[i]).name); // node names + + // compressed bulk node data + MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE, schematic, + nodecount, 2, 2, true); + + fs::safeWriteToFile(filename, ss.str()); +} + + +void build_nnlist_and_update_ids(MapNode *nodes, u32 nodecount, + std::vector *usednodes) { + std::map nodeidmap; + content_t numids = 0; + + for (u32 i = 0; i != nodecount; i++) { + content_t id; + content_t c = nodes[i].getContent(); + + std::map::const_iterator it = nodeidmap.find(c); + if (it == nodeidmap.end()) { + id = numids; + numids++; + + usednodes->push_back(c); + nodeidmap.insert(std::make_pair(c, id)); + } else { + id = it->second; + } + nodes[i].setContent(id); + } +} + + +bool DecoSchematic::getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2) { + ManualMapVoxelManipulator *vm = new ManualMapVoxelManipulator(map); + + v3s16 bp1 = getNodeBlockPos(p1); + v3s16 bp2 = getNodeBlockPos(p2); + vm->initialEmerge(bp1, bp2); + + size = p2 - p1 + 1; + + slice_probs = new u8[size.Y]; + for (s16 y = 0; y != size.Y; y++) + slice_probs[y] = MTSCHEM_PROB_ALWAYS; + + schematic = new MapNode[size.X * size.Y * size.Z]; + + u32 i = 0; + for (s16 z = p1.Z; z <= p2.Z; z++) + for (s16 y = p1.Y; y <= p2.Y; y++) { + u32 vi = vm->m_area.index(p1.X, y, z); + for (s16 x = p1.X; x <= p2.X; x++, i++, vi++) { + schematic[i] = vm->m_data[vi]; + schematic[i].param1 = MTSCHEM_PROB_ALWAYS; + } + } + + delete vm; + return true; +} + + +void DecoSchematic::applyProbabilities(v3s16 p0, + std::vector > *plist, + std::vector > *splist) { + + for (size_t i = 0; i != plist->size(); i++) { + v3s16 p = (*plist)[i].first - p0; + int index = p.Z * (size.Y * size.X) + p.Y * size.X + p.X; + if (index < size.Z * size.Y * size.X) { + u8 prob = (*plist)[i].second; + schematic[index].param1 = prob; + + // trim unnecessary node names from schematic + if (prob == MTSCHEM_PROB_NEVER) + schematic[index].setContent(CONTENT_AIR); + } + } + + for (size_t i = 0; i != splist->size(); i++) { + s16 y = (*splist)[i].first - p0.Y; + slice_probs[y] = (*splist)[i].second; + } +} + + +/////////////////////////////////////////////////////////////////////////////// + + +Mapgen::Mapgen() { + seed = 0; + water_level = 0; + generating = false; + id = -1; + vm = NULL; + ndef = NULL; + heightmap = NULL; + biomemap = NULL; + + for (unsigned int i = 0; i != NUM_GEN_NOTIFY; i++) + gen_notifications[i] = new std::vector; +} + + +Mapgen::~Mapgen() { + for (unsigned int i = 0; i != NUM_GEN_NOTIFY; i++) + delete gen_notifications[i]; +} + + +// Returns Y one under area minimum if not found +s16 Mapgen::findGroundLevelFull(v2s16 p2d) { + v3s16 em = vm->m_area.getExtent(); + s16 y_nodes_max = vm->m_area.MaxEdge.Y; + s16 y_nodes_min = vm->m_area.MinEdge.Y; + u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y); + s16 y; + + for (y = y_nodes_max; y >= y_nodes_min; y--) { + MapNode &n = vm->m_data[i]; + if (ndef->get(n).walkable) + break; + + vm->m_area.add_y(em, i, -1); + } + return (y >= y_nodes_min) ? y : y_nodes_min - 1; +} + + +s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax) { + v3s16 em = vm->m_area.getExtent(); + u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y); + s16 y; + + for (y = ymax; y >= ymin; y--) { + MapNode &n = vm->m_data[i]; + if (ndef->get(n).walkable) + break; + + vm->m_area.add_y(em, i, -1); + } + return y; +} + + +void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax) { + if (!heightmap) + return; + + //TimeTaker t("Mapgen::updateHeightmap", NULL, PRECISION_MICRO); + int index = 0; + for (s16 z = nmin.Z; z <= nmax.Z; z++) { + for (s16 x = nmin.X; x <= nmax.X; x++, index++) { + s16 y = findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y); + + // if the values found are out of range, trust the old heightmap + if (y == nmax.Y && heightmap[index] > nmax.Y) + continue; + if (y == nmin.Y - 1 && heightmap[index] < nmin.Y) + continue; + + heightmap[index] = y; } } + //printf("updateHeightmap: %dus\n", t.stop()); } @@ -222,12 +983,12 @@ void Mapgen::updateLiquid(UniqueQueue *trans_liquid, v3s16 nmin, v3s16 nm for (s16 z = nmin.Z; z <= nmax.Z; z++) { for (s16 x = nmin.X; x <= nmax.X; x++) { wasliquid = true; - + u32 i = vm->m_area.index(x, nmax.Y, z); for (s16 y = nmax.Y; y >= nmin.Y; y--) { isliquid = ndef->get(vm->m_data[i]).isLiquid(); - - // there was a change between liquid and nonliquid, add to queue + + // there was a change between liquid and nonliquid, add to queue. if (isliquid != wasliquid) trans_liquid->push_back(v3s16(x, y, z)); @@ -241,8 +1002,7 @@ void Mapgen::updateLiquid(UniqueQueue *trans_liquid, v3s16 nmin, v3s16 nm void Mapgen::setLighting(v3s16 nmin, v3s16 nmax, u8 light) { ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG); - VoxelArea a(nmin - v3s16(1,0,1) * MAP_BLOCKSIZE, - nmax + v3s16(1,0,1) * MAP_BLOCKSIZE); + VoxelArea a(nmin, nmax); for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) { for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) { @@ -257,7 +1017,7 @@ void Mapgen::setLighting(v3s16 nmin, v3s16 nmax, u8 light) { void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light) { if (light <= 1 || !a.contains(p)) return; - + u32 vi = vm->m_area.index(p); MapNode &nn = vm->m_data[vi]; @@ -265,9 +1025,9 @@ void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light) { // should probably compare masked, but doesn't seem to make a difference if (light <= nn.param1 || !ndef->get(nn).light_propagates) return; - + nn.param1 = light; - + lightSpread(a, p + v3s16(0, 0, 1), light); lightSpread(a, p + v3s16(0, 1, 0), light); lightSpread(a, p + v3s16(1, 0, 0), light); @@ -278,8 +1038,7 @@ void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light) { void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax) { - VoxelArea a(nmin - v3s16(1,0,1) * MAP_BLOCKSIZE, - nmax + v3s16(1,0,1) * MAP_BLOCKSIZE); + VoxelArea a(nmin, nmax); bool block_is_underground = (water_level >= nmax.Y); ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG); @@ -308,7 +1067,7 @@ void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax) { } } } - + // now spread the sunlight and light up any sources for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) { for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) { @@ -318,149 +1077,46 @@ void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax) { if (n.getContent() == CONTENT_IGNORE || !ndef->get(n).light_propagates) continue; - + u8 light_produced = ndef->get(n).light_source & 0x0F; if (light_produced) n.param1 = light_produced; - + u8 light = n.param1 & 0x0F; if (light) { - lightSpread(a, v3s16(x, y, z + 1), light); - lightSpread(a, v3s16(x, y + 1, z ), light); - lightSpread(a, v3s16(x + 1, y, z ), light); - lightSpread(a, v3s16(x, y, z - 1), light); - lightSpread(a, v3s16(x, y - 1, z ), light); - lightSpread(a, v3s16(x - 1, y, z ), light); + lightSpread(a, v3s16(x, y, z + 1), light - 1); + lightSpread(a, v3s16(x, y + 1, z ), light - 1); + lightSpread(a, v3s16(x + 1, y, z ), light - 1); + lightSpread(a, v3s16(x, y, z - 1), light - 1); + lightSpread(a, v3s16(x, y - 1, z ), light - 1); + lightSpread(a, v3s16(x - 1, y, z ), light - 1); } } } } - + //printf("updateLighting: %dms\n", t.stop()); } void Mapgen::calcLightingOld(v3s16 nmin, v3s16 nmax) { enum LightBank banks[2] = {LIGHTBANK_DAY, LIGHTBANK_NIGHT}; - - VoxelArea a(nmin - v3s16(1,0,1) * MAP_BLOCKSIZE, - nmax + v3s16(1,0,1) * MAP_BLOCKSIZE); + VoxelArea a(nmin, nmax); bool block_is_underground = (water_level > nmax.Y); bool sunlight = !block_is_underground; ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG); - + for (int i = 0; i < 2; i++) { enum LightBank bank = banks[i]; std::set light_sources; std::map unlight_from; voxalgo::clearLightAndCollectSources(*vm, a, bank, ndef, - light_sources, unlight_from); + light_sources, unlight_from); voxalgo::propagateSunlight(*vm, a, sunlight, light_sources, ndef); vm->unspreadLight(bank, unlight_from, light_sources, ndef); vm->spreadLight(bank, light_sources, ndef); } } - - -//////////////////////// Mapgen V6 parameter read/write - -bool MapgenV6Params::readParams(Settings *settings) { - freq_desert = settings->getFloat("mgv6_freq_desert"); - freq_beach = settings->getFloat("mgv6_freq_beach"); - - np_terrain_base = settings->getNoiseParams("mgv6_np_terrain_base"); - np_terrain_higher = settings->getNoiseParams("mgv6_np_terrain_higher"); - np_steepness = settings->getNoiseParams("mgv6_np_steepness"); - np_height_select = settings->getNoiseParams("mgv6_np_height_select"); - np_mud = settings->getNoiseParams("mgv6_np_mud"); - np_beach = settings->getNoiseParams("mgv6_np_beach"); - np_biome = settings->getNoiseParams("mgv6_np_biome"); - np_cave = settings->getNoiseParams("mgv6_np_cave"); - np_humidity = settings->getNoiseParams("mgv6_np_humidity"); - np_trees = settings->getNoiseParams("mgv6_np_trees"); - np_apple_trees = settings->getNoiseParams("mgv6_np_apple_trees"); - - bool success = - np_terrain_base && np_terrain_higher && np_steepness && - np_height_select && np_trees && np_mud && - np_beach && np_biome && np_cave && - np_humidity && np_apple_trees; - return success; -} - - -void MapgenV6Params::writeParams(Settings *settings) { - settings->setFloat("mgv6_freq_desert", freq_desert); - settings->setFloat("mgv6_freq_beach", freq_beach); - - settings->setNoiseParams("mgv6_np_terrain_base", np_terrain_base); - settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher); - settings->setNoiseParams("mgv6_np_steepness", np_steepness); - settings->setNoiseParams("mgv6_np_height_select", np_height_select); - settings->setNoiseParams("mgv6_np_mud", np_mud); - settings->setNoiseParams("mgv6_np_beach", np_beach); - settings->setNoiseParams("mgv6_np_biome", np_biome); - settings->setNoiseParams("mgv6_np_cave", np_cave); - settings->setNoiseParams("mgv6_np_humidity", np_humidity); - settings->setNoiseParams("mgv6_np_trees", np_trees); - settings->setNoiseParams("mgv6_np_apple_trees", np_apple_trees); -} - - -bool MapgenV7Params::readParams(Settings *settings) { - np_terrain_base = settings->getNoiseParams("mgv7_np_terrain_base"); - np_terrain_alt = settings->getNoiseParams("mgv7_np_terrain_alt"); - np_terrain_mod = settings->getNoiseParams("mgv7_np_terrain_mod"); - np_terrain_persist = settings->getNoiseParams("mgv7_np_terrain_persist"); - np_height_select = settings->getNoiseParams("mgv7_np_height_select"); - np_ridge = settings->getNoiseParams("mgv7_np_ridge"); - - bool success = - np_terrain_base && np_terrain_alt && np_terrain_mod && - np_terrain_persist && np_height_select && np_ridge; - return success; -} - - -void MapgenV7Params::writeParams(Settings *settings) { - settings->setNoiseParams("mgv7_np_terrain_base", np_terrain_base); - settings->setNoiseParams("mgv7_np_terrain_alt", np_terrain_alt); - settings->setNoiseParams("mgv7_np_terrain_mod", np_terrain_mod); - settings->setNoiseParams("mgv7_np_terrain_persist", np_terrain_persist); - settings->setNoiseParams("mgv7_np_height_select", np_height_select); - settings->setNoiseParams("mgv7_np_ridge", np_ridge); -} - - -/////////////////////////////////// legacy static functions for farmesh - - -s16 Mapgen::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) { - //just need to return something - s16 level = 5; - return level; -} - - -bool Mapgen::get_have_beach(u64 seed, v2s16 p2d) { - double sandnoise = noise2d_perlin( - 0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250, - seed+59420, 3, 0.50); - - return (sandnoise > 0.15); -} - - -double Mapgen::tree_amount_2d(u64 seed, v2s16 p) { - double noise = noise2d_perlin( - 0.5+(float)p.X/125, 0.5+(float)p.Y/125, - seed+2, 4, 0.66); - double zeroval = -0.39; - if(noise < zeroval) - return 0; - else - return 0.04 * (noise-zeroval) / (1.0-zeroval); -}