diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 96693fa4d..335802d85 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -102,7 +102,6 @@ LOCAL_C_INCLUDES := \ LOCAL_SRC_FILES := \ jni/src/ban.cpp \ jni/src/base64.cpp \ - jni/src/biome.cpp \ jni/src/camera.cpp \ jni/src/cavegen.cpp \ jni/src/chat.cpp \ @@ -165,6 +164,10 @@ LOCAL_SRC_FILES := \ jni/src/mapnode.cpp \ jni/src/mapsector.cpp \ jni/src/mesh.cpp \ + jni/src/mg_biome.cpp \ + jni/src/mg_decoration.cpp \ + jni/src/mg_ore.cpp \ + jni/src/mg_schematic.cpp \ jni/src/mods.cpp \ jni/src/nameidmapping.cpp \ jni/src/nodedef.cpp \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4c1522fac..5de5834d3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -335,7 +335,6 @@ add_subdirectory(util) set(common_SRCS ban.cpp base64.cpp - biome.cpp cavegen.cpp clientiface.cpp collision.cpp @@ -373,6 +372,10 @@ set(common_SRCS mapgen_v7.cpp mapnode.cpp mapsector.cpp + mg_biome.cpp + mg_decoration.cpp + mg_ore.cpp + mg_schematic.cpp mods.cpp nameidmapping.cpp nodedef.cpp diff --git a/src/emerge.cpp b/src/emerge.cpp index a4b0752e5..fc0193656 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -39,7 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "profiler.h" #include "log.h" #include "nodedef.h" -#include "biome.h" +#include "mg_biome.h" #include "mapgen_v6.h" #include "mapgen_v7.h" #include "mapgen_singlenode.h" diff --git a/src/emerge.h b/src/emerge.h index b4c8e9ffd..c8b99a6e1 100644 --- a/src/emerge.h +++ b/src/emerge.h @@ -38,8 +38,6 @@ with this program; if not, write to the Free Software Foundation, Inc., infostream << "EmergeThread: " x << std::endl; } class EmergeThread; -//class Mapgen; -//struct MapgenFactory; class Biome; class BiomeDefManager; class Decoration; diff --git a/src/map.cpp b/src/map.cpp index 1fe0ca9ad..7903f3050 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -37,7 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "environment.h" #include "emerge.h" #include "mapgen_v6.h" -#include "biome.h" +#include "mg_biome.h" #include "config.h" #include "server.h" #include "database.h" diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 11f76984d..3627675dc 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapgen.h" #include "voxel.h" #include "noise.h" -#include "biome.h" +#include "mg_biome.h" #include "mapblock.h" #include "mapnode.h" #include "map.h" @@ -32,8 +32,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" // For g_settings #include "main.h" // For g_profiler #include "treegen.h" -#include "mapgen_v6.h" -#include "mapgen_v7.h" #include "serialization.h" #include "util/serialize.h" #include "filesys.h" @@ -49,20 +47,6 @@ FlagDesc flagdesc_mapgen[] = { {NULL, 0} }; -FlagDesc flagdesc_ore[] = { - {"absheight", OREFLAG_ABSHEIGHT}, - {"scatter_noisedensity", OREFLAG_DENSITY}, - {"claylike_nodeisnt", OREFLAG_NODEISNT}, - {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}, @@ -76,759 +60,12 @@ FlagDesc flagdesc_gennotify[] = { /////////////////////////////////////////////////////////////////////////////// -Ore *createOre(OreType type) { - switch (type) { - case ORE_SCATTER: - return new OreScatter; - case ORE_SHEET: - return new OreSheet; - //case ORE_CLAYLIKE: //TODO: implement this! - // return new OreClaylike; - default: - return NULL; - } -} -Ore::~Ore() { - delete np; - delete noise; -} - - -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); - if (flags & OREFLAG_ABSHEIGHT) - in_range |= (nmin.Y >= -height_max && nmax.Y <= -height_min) << 1; - if (!in_range) - return; - - 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); - } - 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(c_ore, 0, ore_param2); - - int volume = (nmax.X - nmin.X + 1) * - (nmax.Y - nmin.Y + 1) * - (nmax.Z - nmin.Z + 1); - int csize = clust_size; - int orechance = (csize * csize * csize) / clust_num_ores; - int nclusters = volume / clust_scarcity; - - for (int i = 0; i != nclusters; i++) { - int x0 = pr.range(nmin.X, nmax.X - 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, 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 (!CONTAINS(c_wherein, vm->m_data[i].getContent())) - continue; - - vm->m_data[i] = n_ore; - } - } -} - - -void OreSheet::generate(ManualMapVoxelManipulator *vm, int seed, - u32 blockseed, v3s16 nmin, v3s16 nmax) { - PseudoRandom pr(blockseed + 4234); - MapNode n_ore(c_ore, 0, ore_param2); - - int max_height = clust_size; - 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 = seed + y_start; - noise->perlinMap2D(nmin.X, nmin.Z); - - int index = 0; - 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; - for (int y = y0; y != y1; y++) { - u32 i = vm->m_area.index(x, y, z); - if (!vm->m_area.contains(i)) - continue; - if (!CONTAINS(c_wherein, vm->m_data[i].getContent())) - continue; - - 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::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 - - -/////////////////////////////////////////////////////////////////////////////// - -bool DecoSimple::canPlaceDecoration(ManualMapVoxelManipulator *vm, v3s16 p) { - // Don't bother if there aren't any decorations to place - if (c_decos.size() == 0) - return false; - - u32 vi = vm->m_area.index(p); - - // Check if the decoration can be placed on this node - if (!CONTAINS(c_place_on, vm->m_data[vi].getContent())) - return false; - - // Don't continue if there are no spawnby constraints - if (nspawnby == -1) - return true; - - int nneighs = 0; - v3s16 dirs[8] = { - 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) - }; - - // Check a Moore neighborhood if there are enough spawnby nodes - for (size_t i = 0; i != ARRLEN(dirs); i++) { - u32 index = vm->m_area.index(p + dirs[i]); - if (!vm->m_area.contains(index)) - continue; - - if (CONTAINS(c_spawnby, vm->m_data[index].getContent())) - nneighs++; - } - - if (nneighs < nspawnby) - return false; - - return true; -} - - -void DecoSimple::generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p) { - ManualMapVoxelManipulator *vm = mg->vm; - - if (!canPlaceDecoration(vm, p)) - return; - - content_t c_place = c_decos[pr->range(0, c_decos.size() - 1)]; - - 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(); - u32 vi = vm->m_area.index(p); - 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 ""; -} - - -/////////////////////////////////////////////////////////////////////////////// - - -DecoSchematic::DecoSchematic() { - schematic = NULL; - slice_probs = NULL; - flags = 0; - size = v3s16(0, 0, 0); -} - - -DecoSchematic::~DecoSchematic() { - delete []schematic; - delete []slice_probs; -} - - -void DecoSchematic::updateContentIds() { - if (flags & DECO_SCHEM_CIDS_UPDATED) - return; - - flags |= DECO_SCHEM_CIDS_UPDATED; - - for (int i = 0; i != size.X * size.Y * size.Z; i++) - schematic[i].setContent(c_nodes[schematic[i].getContent()]); -} - - -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); - content_t c = vm->m_data[vi].getContent(); - if (!CONTAINS(c_place_on, c)) - 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; - - updateContentIds(); - - 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(NodeResolver *resolver, - std::map &replace_names) -{ - 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); - - for (int i = 0; i != nidmapcount; i++) { - std::string name = deSerializeString(is); - if (name == "ignore") { - name = "air"; - cignore = i; - have_cignore = true; - } - - std::map::iterator it; - - it = replace_names.find(name); - if (it != replace_names.end()) - name = it->second; - - resolver->addNodeList(name.c_str(), &c_nodes); - } - - 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; diff --git a/src/mapgen.h b/src/mapgen.h index b6efe8200..e29616342 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -37,30 +37,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MG_FLAT 0x08 #define MG_LIGHT 0x10 -/////////////////// Ore generation flags -// Use absolute value of height to determine ore placement -#define OREFLAG_ABSHEIGHT 0x01 -// Use 3d noise to get density of ore placement, instead of just the position -#define OREFLAG_DENSITY 0x02 // not yet implemented -// For claylike ore types, place ore if the number of surrounding -// nodes isn't the specified node -#define OREFLAG_NODEISNT 0x04 // not yet implemented - -/////////////////// Decoration flags -#define DECO_PLACE_CENTER_X 1 -#define DECO_PLACE_CENTER_Y 2 -#define DECO_PLACE_CENTER_Z 4 -#define DECO_SCHEM_CIDS_UPDATED 8 - -#define ORE_RANGE_ACTUAL 1 -#define ORE_RANGE_MIRROR 2 - #define NUM_GEN_NOTIFY 6 extern FlagDesc flagdesc_mapgen[]; -extern FlagDesc flagdesc_ore[]; -extern FlagDesc flagdesc_deco_schematic[]; extern FlagDesc flagdesc_gennotify[]; class BiomeDefManager; @@ -92,13 +72,6 @@ enum GenNotify { GENNOTIFY_LARGECAVE_END }; -enum OreType { - ORE_SCATTER, - ORE_SHEET, - ORE_CLAYLIKE -}; - - struct MapgenSpecificParams { virtual void readParams(Settings *settings) = 0; virtual void writeParams(Settings *settings) = 0; @@ -163,163 +136,5 @@ struct MapgenFactory { virtual ~MapgenFactory() {} }; -class Ore { -public: - content_t c_ore; // the node to place - std::vector c_wherein; // the nodes to be placed in - u32 clust_scarcity; // ore cluster has a 1-in-clust_scarcity chance of appearing at a node - s16 clust_num_ores; // how many ore nodes are in a chunk - s16 clust_size; // how large (in nodes) a chunk of ore is - s16 height_min; - s16 height_max; - u8 ore_param2; // to set node-specific attributes - u32 flags; // attributes for this ore - float nthresh; // threshhold for noise at which an ore is placed - NoiseParams *np; // noise for distribution of clusters (NULL for uniform scattering) - Noise *noise; - - Ore() { - c_ore = CONTENT_IGNORE; - np = NULL; - noise = NULL; - } - - virtual ~Ore(); - - void placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); - virtual void generate(ManualMapVoxelManipulator *vm, int seed, - u32 blockseed, v3s16 nmin, v3s16 nmax) = 0; -}; - -class OreScatter : public Ore { - ~OreScatter() {} - virtual void generate(ManualMapVoxelManipulator *vm, int seed, - u32 blockseed, v3s16 nmin, v3s16 nmax); -}; - -class OreSheet : public Ore { - ~OreSheet() {} - virtual void generate(ManualMapVoxelManipulator *vm, int seed, - u32 blockseed, v3s16 nmin, v3s16 nmax); -}; - -Ore *createOre(OreType type); - - -enum DecorationType { - DECO_SIMPLE, - DECO_SCHEMATIC, - DECO_LSYSTEM -}; - -#if 0 -struct CutoffData { - VoxelArea a; - Decoration *deco; - //v3s16 p; - //v3s16 size; - //s16 height; - - CutoffData(s16 x, s16 y, s16 z, s16 h) { - p = v3s16(x, y, z); - height = h; - } -}; -#endif - -class Decoration { -public: - INodeDefManager *ndef; - - int mapseed; - std::vector c_place_on; - s16 sidelen; - float fill_ratio; - NoiseParams *np; - - std::set biomes; - //std::list cutoffs; - //JMutex cutoff_mutex; - - Decoration(); - virtual ~Decoration(); - - void placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); - void placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); - - virtual void generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p) = 0; - virtual int getHeight() = 0; - virtual std::string getName() = 0; -}; - -class DecoSimple : public Decoration { -public: - std::vector c_decos; - std::vector c_spawnby; - s16 deco_height; - s16 deco_height_max; - s16 nspawnby; - - ~DecoSimple() {} - - bool canPlaceDecoration(ManualMapVoxelManipulator *vm, v3s16 p); - virtual void generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p); - virtual int getHeight(); - virtual std::string getName(); -}; - -#define MTSCHEM_FILE_SIGNATURE 0x4d54534d // 'MTSM' -#define MTSCHEM_FILE_VER_HIGHEST_READ 3 -#define MTSCHEM_FILE_VER_HIGHEST_WRITE 3 - -#define MTSCHEM_PROB_NEVER 0x00 -#define MTSCHEM_PROB_ALWAYS 0xFF - -class DecoSchematic : public Decoration { -public: - std::string filename; - - std::vector c_nodes; - - u32 flags; - Rotation rotation; - v3s16 size; - MapNode *schematic; - u8 *slice_probs; - - DecoSchematic(); - ~DecoSchematic(); - - void updateContentIds(); - virtual void generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p); - virtual int getHeight(); - virtual std::string getName(); - - void blitToVManip(v3s16 p, ManualMapVoxelManipulator *vm, - Rotation rot, bool force_placement); - - bool loadSchematicFile(NodeResolver *resolver, - std::map &replace_names); - void saveSchematicFile(INodeDefManager *ndef); - - bool getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2); - void placeStructure(Map *map, v3s16 p, bool force_placement); - void applyProbabilities(v3s16 p0, - std::vector > *plist, - std::vector > *splist); -}; - -void build_nnlist_and_update_ids(MapNode *nodes, u32 nodecount, - std::vector *usednodes); - -/* -class DecoLSystem : public Decoration { -public: - virtual void generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); -}; -*/ - -Decoration *createDecoration(DecorationType type); - #endif diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index 227b57311..c126165dc 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -35,6 +35,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "dungeongen.h" #include "cavegen.h" #include "treegen.h" +#include "mg_ore.h" +#include "mg_decoration.h" #include "mapgen_v6.h" FlagDesc flagdesc_mapgen_v6[] = { diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index 332865304..77aff6e06 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -34,7 +34,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "dungeongen.h" #include "cavegen.h" #include "treegen.h" -#include "biome.h" +#include "mg_biome.h" +#include "mg_ore.h" +#include "mg_decoration.h" #include "mapgen_v7.h" diff --git a/src/biome.cpp b/src/mg_biome.cpp similarity index 99% rename from src/biome.cpp rename to src/mg_biome.cpp index fc92ad603..b1172a09d 100644 --- a/src/biome.cpp +++ b/src/mg_biome.cpp @@ -17,7 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "biome.h" +#include "mg_biome.h" #include "nodedef.h" #include "map.h" //for ManualMapVoxelManipulator #include "log.h" diff --git a/src/biome.h b/src/mg_biome.h similarity index 97% rename from src/biome.h rename to src/mg_biome.h index 554a61f4e..3724652bd 100644 --- a/src/biome.h +++ b/src/mg_biome.h @@ -17,8 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef BIOME_HEADER -#define BIOME_HEADER +#ifndef MG_BIOME_HEADER +#define MG_BIOME_HEADER #include #include "nodedef.h" diff --git a/src/mg_decoration.cpp b/src/mg_decoration.cpp new file mode 100644 index 000000000..7f6126bfc --- /dev/null +++ b/src/mg_decoration.cpp @@ -0,0 +1,275 @@ +/* +Minetest +Copyright (C) 2010-2014 kwolekr, Ryan Kwolek + +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 +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "mg_decoration.h" +#include "mg_schematic.h" +#include "mapgen.h" +#include "map.h" +#include "log.h" +#include "util/numeric.h" + +/////////////////////////////////////////////////////////////////////////////// + + +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::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 + + +/////////////////////////////////////////////////////////////////////////////// + +bool DecoSimple::canPlaceDecoration(ManualMapVoxelManipulator *vm, v3s16 p) +{ + // Don't bother if there aren't any decorations to place + if (c_decos.size() == 0) + return false; + + u32 vi = vm->m_area.index(p); + + // Check if the decoration can be placed on this node + if (!CONTAINS(c_place_on, vm->m_data[vi].getContent())) + return false; + + // Don't continue if there are no spawnby constraints + if (nspawnby == -1) + return true; + + int nneighs = 0; + v3s16 dirs[8] = { + 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) + }; + + // Check a Moore neighborhood if there are enough spawnby nodes + for (size_t i = 0; i != ARRLEN(dirs); i++) { + u32 index = vm->m_area.index(p + dirs[i]); + if (!vm->m_area.contains(index)) + continue; + + if (CONTAINS(c_spawnby, vm->m_data[index].getContent())) + nneighs++; + } + + if (nneighs < nspawnby) + return false; + + return true; +} + + +void DecoSimple::generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p) +{ + ManualMapVoxelManipulator *vm = mg->vm; + + if (!canPlaceDecoration(vm, p)) + return; + + content_t c_place = c_decos[pr->range(0, c_decos.size() - 1)]; + + 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(); + u32 vi = vm->m_area.index(p); + 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 ""; +} diff --git a/src/mg_decoration.h b/src/mg_decoration.h new file mode 100644 index 000000000..78ff9dc2e --- /dev/null +++ b/src/mg_decoration.h @@ -0,0 +1,102 @@ +/* +Minetest +Copyright (C) 2010-2013 kwolekr, Ryan Kwolek + +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 +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef MG_DECORATION_HEADER +#define MG_DECORATION_HEADER + +#include +#include "mapnode.h" + +class NoiseParams; +class Mapgen; +class ManualMapVoxelManipulator; +class PseudoRandom; + +enum DecorationType { + DECO_SIMPLE, + DECO_SCHEMATIC, + DECO_LSYSTEM +}; + +#if 0 +struct CutoffData { + VoxelArea a; + Decoration *deco; + //v3s16 p; + //v3s16 size; + //s16 height; + + CutoffData(s16 x, s16 y, s16 z, s16 h) { + p = v3s16(x, y, z); + height = h; + } +}; +#endif + +class Decoration { +public: + INodeDefManager *ndef; + + int mapseed; + std::vector c_place_on; + s16 sidelen; + float fill_ratio; + NoiseParams *np; + + std::set biomes; + //std::list cutoffs; + //JMutex cutoff_mutex; + + Decoration(); + virtual ~Decoration(); + + void placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); + void placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); + + virtual void generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p) = 0; + virtual int getHeight() = 0; + virtual std::string getName() = 0; +}; + +class DecoSimple : public Decoration { +public: + std::vector c_decos; + std::vector c_spawnby; + s16 deco_height; + s16 deco_height_max; + s16 nspawnby; + + ~DecoSimple() {} + + bool canPlaceDecoration(ManualMapVoxelManipulator *vm, v3s16 p); + virtual void generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p); + virtual int getHeight(); + virtual std::string getName(); +}; + +/* +class DecoLSystem : public Decoration { +public: + virtual void generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); +}; +*/ + +Decoration *createDecoration(DecorationType type); + +#endif diff --git a/src/mg_ore.cpp b/src/mg_ore.cpp new file mode 100644 index 000000000..c8d224dc6 --- /dev/null +++ b/src/mg_ore.cpp @@ -0,0 +1,161 @@ +/* +Minetest +Copyright (C) 2010-2014 kwolekr, Ryan Kwolek + +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 +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "mg_ore.h" +#include "mapgen.h" +#include "util/numeric.h" +#include "map.h" +#include "log.h" + + +FlagDesc flagdesc_ore[] = { + {"absheight", OREFLAG_ABSHEIGHT}, + {"scatter_noisedensity", OREFLAG_DENSITY}, + {"claylike_nodeisnt", OREFLAG_NODEISNT}, + {NULL, 0} +}; + +/////////////////////////////////////////////////////////////////////////////// + + +Ore *createOre(OreType type) +{ + switch (type) { + case ORE_SCATTER: + return new OreScatter; + case ORE_SHEET: + return new OreSheet; + //case ORE_CLAYLIKE: //TODO: implement this! + // return new OreClaylike; + default: + return NULL; + } +} + + +Ore::~Ore() +{ + delete np; + delete noise; +} + + +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); + if (flags & OREFLAG_ABSHEIGHT) + in_range |= (nmin.Y >= -height_max && nmax.Y <= -height_min) << 1; + if (!in_range) + return; + + 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); + } + 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(c_ore, 0, ore_param2); + + int volume = (nmax.X - nmin.X + 1) * + (nmax.Y - nmin.Y + 1) * + (nmax.Z - nmin.Z + 1); + int csize = clust_size; + int orechance = (csize * csize * csize) / clust_num_ores; + int nclusters = volume / clust_scarcity; + + for (int i = 0; i != nclusters; i++) { + int x0 = pr.range(nmin.X, nmax.X - 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, 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 (!CONTAINS(c_wherein, vm->m_data[i].getContent())) + continue; + + vm->m_data[i] = n_ore; + } + } +} + + +void OreSheet::generate(ManualMapVoxelManipulator *vm, int seed, + u32 blockseed, v3s16 nmin, v3s16 nmax) +{ + PseudoRandom pr(blockseed + 4234); + MapNode n_ore(c_ore, 0, ore_param2); + + int max_height = clust_size; + 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 = seed + y_start; + noise->perlinMap2D(nmin.X, nmin.Z); + + int index = 0; + 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; + for (int y = y0; y != y1; y++) { + u32 i = vm->m_area.index(x, y, z); + if (!vm->m_area.contains(i)) + continue; + if (!CONTAINS(c_wherein, vm->m_data[i].getContent())) + continue; + + vm->m_data[i] = n_ore; + } + } +} + diff --git a/src/mg_ore.h b/src/mg_ore.h new file mode 100644 index 000000000..c279703a8 --- /dev/null +++ b/src/mg_ore.h @@ -0,0 +1,94 @@ +/* +Minetest +Copyright (C) 2010-2013 kwolekr, Ryan Kwolek + +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 +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef MG_ORE_HEADER +#define MG_ORE_HEADER + +#include "util/string.h" +#include "mapnode.h" + +class NoiseParams; +class Noise; +class Mapgen; +class ManualMapVoxelManipulator; + +/////////////////// Ore generation flags +// Use absolute value of height to determine ore placement +#define OREFLAG_ABSHEIGHT 0x01 +// Use 3d noise to get density of ore placement, instead of just the position +#define OREFLAG_DENSITY 0x02 // not yet implemented +// For claylike ore types, place ore if the number of surrounding +// nodes isn't the specified node +#define OREFLAG_NODEISNT 0x04 // not yet implemented + +#define ORE_RANGE_ACTUAL 1 +#define ORE_RANGE_MIRROR 2 + +extern FlagDesc flagdesc_ore[]; + +enum OreType { + ORE_SCATTER, + ORE_SHEET, + ORE_CLAYLIKE +}; + +class Ore { +public: + content_t c_ore; // the node to place + std::vector c_wherein; // the nodes to be placed in + u32 clust_scarcity; // ore cluster has a 1-in-clust_scarcity chance of appearing at a node + s16 clust_num_ores; // how many ore nodes are in a chunk + s16 clust_size; // how large (in nodes) a chunk of ore is + s16 height_min; + s16 height_max; + u8 ore_param2; // to set node-specific attributes + u32 flags; // attributes for this ore + float nthresh; // threshhold for noise at which an ore is placed + NoiseParams *np; // noise for distribution of clusters (NULL for uniform scattering) + Noise *noise; + + Ore() { + c_ore = CONTENT_IGNORE; + np = NULL; + noise = NULL; + } + + virtual ~Ore(); + + void placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); + virtual void generate(ManualMapVoxelManipulator *vm, int seed, + u32 blockseed, v3s16 nmin, v3s16 nmax) = 0; +}; + +class OreScatter : public Ore { + ~OreScatter() {} + virtual void generate(ManualMapVoxelManipulator *vm, int seed, + u32 blockseed, v3s16 nmin, v3s16 nmax); +}; + +class OreSheet : public Ore { + ~OreSheet() {} + virtual void generate(ManualMapVoxelManipulator *vm, int seed, + u32 blockseed, v3s16 nmin, v3s16 nmax); +}; + +Ore *createOre(OreType type); + + +#endif diff --git a/src/mg_schematic.cpp b/src/mg_schematic.cpp new file mode 100644 index 000000000..8b34fb4fd --- /dev/null +++ b/src/mg_schematic.cpp @@ -0,0 +1,432 @@ +/* +Minetest +Copyright (C) 2010-2014 kwolekr, Ryan Kwolek + +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 +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include "mg_schematic.h" +#include "mapgen.h" +#include "map.h" +#include "mapblock.h" +#include "log.h" +#include "util/numeric.h" +#include "util/serialize.h" +#include "serialization.h" +#include "filesys.h" + +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} +}; + +/////////////////////////////////////////////////////////////////////////////// + + +DecoSchematic::DecoSchematic() +{ + schematic = NULL; + slice_probs = NULL; + flags = 0; + size = v3s16(0, 0, 0); +} + + +DecoSchematic::~DecoSchematic() +{ + delete []schematic; + delete []slice_probs; +} + + +void DecoSchematic::updateContentIds() +{ + if (flags & DECO_SCHEM_CIDS_UPDATED) + return; + + flags |= DECO_SCHEM_CIDS_UPDATED; + + for (int i = 0; i != size.X * size.Y * size.Z; i++) + schematic[i].setContent(c_nodes[schematic[i].getContent()]); +} + + +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); + content_t c = vm->m_data[vi].getContent(); + if (!CONTAINS(c_place_on, c)) + 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; + + updateContentIds(); + + 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(NodeResolver *resolver, + std::map &replace_names) +{ + 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); + + for (int i = 0; i != nidmapcount; i++) { + std::string name = deSerializeString(is); + if (name == "ignore") { + name = "air"; + cignore = i; + have_cignore = true; + } + + std::map::iterator it; + + it = replace_names.find(name); + if (it != replace_names.end()) + name = it->second; + + resolver->addNodeList(name.c_str(), &c_nodes); + } + + 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; + } +} diff --git a/src/mg_schematic.h b/src/mg_schematic.h new file mode 100644 index 000000000..8a495fef2 --- /dev/null +++ b/src/mg_schematic.h @@ -0,0 +1,87 @@ +/* +Minetest +Copyright (C) 2010-2013 kwolekr, Ryan Kwolek + +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 +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef MG_SCHEMATIC_HEADER +#define MG_SCHEMATIC_HEADER + +#include +#include "mg_decoration.h" +#include "util/string.h" + +class Map; +class Mapgen; +class ManualMapVoxelManipulator; +class PseudoRandom; +class NodeResolver; + +/////////////////// Decoration flags +#define DECO_PLACE_CENTER_X 1 +#define DECO_PLACE_CENTER_Y 2 +#define DECO_PLACE_CENTER_Z 4 +#define DECO_SCHEM_CIDS_UPDATED 8 + + +#define MTSCHEM_FILE_SIGNATURE 0x4d54534d // 'MTSM' +#define MTSCHEM_FILE_VER_HIGHEST_READ 3 +#define MTSCHEM_FILE_VER_HIGHEST_WRITE 3 + +#define MTSCHEM_PROB_NEVER 0x00 +#define MTSCHEM_PROB_ALWAYS 0xFF + +extern FlagDesc flagdesc_deco_schematic[]; + +class DecoSchematic : public Decoration { +public: + std::string filename; + + std::vector c_nodes; + + u32 flags; + Rotation rotation; + v3s16 size; + MapNode *schematic; + u8 *slice_probs; + + DecoSchematic(); + ~DecoSchematic(); + + void updateContentIds(); + virtual void generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p); + virtual int getHeight(); + virtual std::string getName(); + + void blitToVManip(v3s16 p, ManualMapVoxelManipulator *vm, + Rotation rot, bool force_placement); + + bool loadSchematicFile(NodeResolver *resolver, + std::map &replace_names); + void saveSchematicFile(INodeDefManager *ndef); + + bool getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2); + void placeStructure(Map *map, v3s16 p, bool force_placement); + void applyProbabilities(v3s16 p0, + std::vector > *plist, + std::vector > *splist); +}; + +void build_nnlist_and_update_ids(MapNode *nodes, u32 nodecount, + std::vector *usednodes); + + +#endif diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 58d8c473e..88bbedec5 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -31,7 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "tool.h" #include "serverobject.h" #include "porting.h" -#include "mapgen.h" +#include "mg_schematic.h" +#include "noise.h" #include "json/json.h" struct EnumString es_TileAnimationType[] = diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index fdbeafb31..2ed9fadf9 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -25,8 +25,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/serialize.h" #include "server.h" #include "environment.h" -#include "biome.h" +#include "mg_biome.h" #include "emerge.h" +#include "mg_biome.h" +#include "mg_ore.h" +#include "mg_decoration.h" +#include "mg_schematic.h" #include "mapgen_v7.h" #include "main.h" #include "log.h" diff --git a/src/server.cpp b/src/server.cpp index 812ab6410..2558d0d6f 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -44,7 +44,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "craftdef.h" #include "emerge.h" #include "mapgen.h" -#include "biome.h" +#include "mg_biome.h" #include "content_mapnode.h" #include "content_nodemeta.h" #include "content_abm.h"