LCOV - code coverage report
Current view: top level - src - mg_schematic.cpp (source / functions) Hit Total Coverage
Test: report Lines: 1 300 0.3 %
Date: 2015-07-11 18:23:49 Functions: 2 18 11.1 %

          Line data    Source code
       1             : /*
       2             : Minetest
       3             : Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
       4             : 
       5             : This program is free software; you can redistribute it and/or modify
       6             : it under the terms of the GNU Lesser General Public License as published by
       7             : the Free Software Foundation; either version 2.1 of the License, or
       8             : (at your option) any later version.
       9             : 
      10             : This program is distributed in the hope that it will be useful,
      11             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             : GNU Lesser General Public License for more details.
      14             : 
      15             : You should have received a copy of the GNU Lesser General Public License along
      16             : with this program; if not, write to the Free Software Foundation, Inc.,
      17             : 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      18             : */
      19             : 
      20             : #include <fstream>
      21             : #include <typeinfo>
      22             : #include "mg_schematic.h"
      23             : #include "gamedef.h"
      24             : #include "mapgen.h"
      25             : #include "emerge.h"
      26             : #include "map.h"
      27             : #include "mapblock.h"
      28             : #include "log.h"
      29             : #include "util/numeric.h"
      30             : #include "util/serialize.h"
      31             : #include "serialization.h"
      32             : #include "filesys.h"
      33             : 
      34             : ///////////////////////////////////////////////////////////////////////////////
      35             : 
      36             : 
      37           0 : SchematicManager::SchematicManager(IGameDef *gamedef) :
      38           0 :         ObjDefManager(gamedef, OBJDEF_SCHEMATIC)
      39             : {
      40           0 :         m_gamedef = gamedef;
      41           0 : }
      42             : 
      43             : 
      44           0 : void SchematicManager::clear()
      45             : {
      46           0 :         EmergeManager *emerge = m_gamedef->getEmergeManager();
      47             : 
      48             :         // Remove all dangling references in Decorations
      49           0 :         DecorationManager *decomgr = emerge->decomgr;
      50           0 :         for (size_t i = 0; i != decomgr->getNumObjects(); i++) {
      51           0 :                 Decoration *deco = (Decoration *)decomgr->getRaw(i);
      52             : 
      53             :                 try {
      54           0 :                         DecoSchematic *dschem = dynamic_cast<DecoSchematic *>(deco);
      55           0 :                         if (dschem)
      56           0 :                                 dschem->schematic = NULL;
      57             :                 } catch (std::bad_cast) {
      58             :                 }
      59             :         }
      60             : 
      61           0 :         ObjDefManager::clear();
      62           0 : }
      63             : 
      64             : 
      65             : ///////////////////////////////////////////////////////////////////////////////
      66             : 
      67             : 
      68           0 : Schematic::Schematic()
      69             : {
      70           0 :         schemdata   = NULL;
      71           0 :         slice_probs = NULL;
      72           0 :         flags       = 0;
      73           0 :         size        = v3s16(0, 0, 0);
      74           0 : }
      75             : 
      76             : 
      77           0 : Schematic::~Schematic()
      78             : {
      79           0 :         delete []schemdata;
      80           0 :         delete []slice_probs;
      81           0 : }
      82             : 
      83             : 
      84           0 : void Schematic::resolveNodeNames()
      85             : {
      86           0 :         getIdsFromNrBacklog(&c_nodes, true, CONTENT_AIR);
      87             : 
      88           0 :         size_t bufsize = size.X * size.Y * size.Z;
      89           0 :         for (size_t i = 0; i != bufsize; i++) {
      90           0 :                 content_t c_original = schemdata[i].getContent();
      91           0 :                 content_t c_new = c_nodes[c_original];
      92           0 :                 schemdata[i].setContent(c_new);
      93             :         }
      94           0 : }
      95             : 
      96             : 
      97           0 : void Schematic::blitToVManip(v3s16 p, MMVManip *vm, Rotation rot, bool force_place)
      98             : {
      99           0 :         sanity_check(m_ndef != NULL);
     100             : 
     101           0 :         int xstride = 1;
     102           0 :         int ystride = size.X;
     103           0 :         int zstride = size.X * size.Y;
     104             : 
     105           0 :         s16 sx = size.X;
     106           0 :         s16 sy = size.Y;
     107           0 :         s16 sz = size.Z;
     108             : 
     109             :         int i_start, i_step_x, i_step_z;
     110           0 :         switch (rot) {
     111             :                 case ROTATE_90:
     112           0 :                         i_start  = sx - 1;
     113           0 :                         i_step_x = zstride;
     114           0 :                         i_step_z = -xstride;
     115           0 :                         SWAP(s16, sx, sz);
     116           0 :                         break;
     117             :                 case ROTATE_180:
     118           0 :                         i_start  = zstride * (sz - 1) + sx - 1;
     119           0 :                         i_step_x = -xstride;
     120           0 :                         i_step_z = -zstride;
     121           0 :                         break;
     122             :                 case ROTATE_270:
     123           0 :                         i_start  = zstride * (sz - 1);
     124           0 :                         i_step_x = -zstride;
     125           0 :                         i_step_z = xstride;
     126           0 :                         SWAP(s16, sx, sz);
     127           0 :                         break;
     128             :                 default:
     129           0 :                         i_start  = 0;
     130           0 :                         i_step_x = xstride;
     131           0 :                         i_step_z = zstride;
     132             :         }
     133             : 
     134           0 :         s16 y_map = p.Y;
     135           0 :         for (s16 y = 0; y != sy; y++) {
     136           0 :                 if ((slice_probs[y] != MTSCHEM_PROB_ALWAYS) &&
     137           0 :                         (slice_probs[y] <= myrand_range(1, MTSCHEM_PROB_ALWAYS)))
     138           0 :                         continue;
     139             : 
     140           0 :                 for (s16 z = 0; z != sz; z++) {
     141           0 :                         u32 i = z * i_step_z + y * ystride + i_start;
     142           0 :                         for (s16 x = 0; x != sx; x++, i += i_step_x) {
     143           0 :                                 u32 vi = vm->m_area.index(p.X + x, y_map, p.Z + z);
     144           0 :                                 if (!vm->m_area.contains(vi))
     145           0 :                                         continue;
     146             : 
     147           0 :                                 if (schemdata[i].getContent() == CONTENT_IGNORE)
     148           0 :                                         continue;
     149             : 
     150           0 :                                 u8 placement_prob     = schemdata[i].param1 & MTSCHEM_PROB_MASK;
     151           0 :                                 bool force_place_node = schemdata[i].param1 & MTSCHEM_FORCE_PLACE;
     152             : 
     153           0 :                                 if (placement_prob == MTSCHEM_PROB_NEVER)
     154           0 :                                         continue;
     155             : 
     156           0 :                                 if (!force_place && !force_place_node) {
     157           0 :                                         content_t c = vm->m_data[vi].getContent();
     158           0 :                                         if (c != CONTENT_AIR && c != CONTENT_IGNORE)
     159           0 :                                                 continue;
     160             :                                 }
     161             : 
     162           0 :                                 if ((placement_prob != MTSCHEM_PROB_ALWAYS) &&
     163           0 :                                         (placement_prob <= myrand_range(1, MTSCHEM_PROB_ALWAYS)))
     164           0 :                                         continue;
     165             : 
     166           0 :                                 vm->m_data[vi] = schemdata[i];
     167           0 :                                 vm->m_data[vi].param1 = 0;
     168             : 
     169           0 :                                 if (rot)
     170           0 :                                         vm->m_data[vi].rotateAlongYAxis(m_ndef, rot);
     171             :                         }
     172             :                 }
     173           0 :                 y_map++;
     174             :         }
     175           0 : }
     176             : 
     177             : 
     178           0 : void Schematic::placeStructure(Map *map, v3s16 p, u32 flags,
     179             :         Rotation rot, bool force_place)
     180             : {
     181             :         assert(schemdata != NULL); // Pre-condition
     182           0 :         sanity_check(m_ndef != NULL);
     183             : 
     184           0 :         MMVManip *vm = new MMVManip(map);
     185             : 
     186           0 :         if (rot == ROTATE_RAND)
     187           0 :                 rot = (Rotation)myrand_range(ROTATE_0, ROTATE_270);
     188             : 
     189           0 :         v3s16 s = (rot == ROTATE_90 || rot == ROTATE_270) ?
     190           0 :                                 v3s16(size.Z, size.Y, size.X) : size;
     191             : 
     192           0 :         if (flags & DECO_PLACE_CENTER_X)
     193           0 :                 p.X -= (s.X + 1) / 2;
     194           0 :         if (flags & DECO_PLACE_CENTER_Y)
     195           0 :                 p.Y -= (s.Y + 1) / 2;
     196           0 :         if (flags & DECO_PLACE_CENTER_Z)
     197           0 :                 p.Z -= (s.Z + 1) / 2;
     198             : 
     199           0 :         v3s16 bp1 = getNodeBlockPos(p);
     200           0 :         v3s16 bp2 = getNodeBlockPos(p + s - v3s16(1,1,1));
     201           0 :         vm->initialEmerge(bp1, bp2);
     202             : 
     203           0 :         blitToVManip(p, vm, rot, force_place);
     204             : 
     205           0 :         std::map<v3s16, MapBlock *> lighting_modified_blocks;
     206           0 :         std::map<v3s16, MapBlock *> modified_blocks;
     207           0 :         vm->blitBackAll(&modified_blocks);
     208             : 
     209             :         // TODO: Optimize this by using Mapgen::calcLighting() instead
     210           0 :         lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
     211           0 :         map->updateLighting(lighting_modified_blocks, modified_blocks);
     212             : 
     213           0 :         MapEditEvent event;
     214           0 :         event.type = MEET_OTHER;
     215           0 :         for (std::map<v3s16, MapBlock *>::iterator
     216           0 :                 it = modified_blocks.begin();
     217           0 :                 it != modified_blocks.end(); ++it)
     218           0 :                 event.modified_blocks.insert(it->first);
     219             : 
     220           0 :         map->dispatchEvent(&event);
     221           0 : }
     222             : 
     223             : 
     224           0 : bool Schematic::deserializeFromMts(std::istream *is,
     225             :         std::vector<std::string> *names)
     226             : {
     227           0 :         std::istream &ss = *is;
     228           0 :         content_t cignore = CONTENT_IGNORE;
     229           0 :         bool have_cignore = false;
     230             : 
     231             :         //// Read signature
     232           0 :         u32 signature = readU32(ss);
     233           0 :         if (signature != MTSCHEM_FILE_SIGNATURE) {
     234             :                 errorstream << "Schematic::deserializeFromMts: invalid schematic "
     235           0 :                         "file" << std::endl;
     236           0 :                 return false;
     237             :         }
     238             : 
     239             :         //// Read version
     240           0 :         u16 version = readU16(ss);
     241           0 :         if (version > MTSCHEM_FILE_VER_HIGHEST_READ) {
     242             :                 errorstream << "Schematic::deserializeFromMts: unsupported schematic "
     243           0 :                         "file version" << std::endl;
     244           0 :                 return false;
     245             :         }
     246             : 
     247             :         //// Read size
     248           0 :         size = readV3S16(ss);
     249             : 
     250             :         //// Read Y-slice probability values
     251           0 :         delete []slice_probs;
     252           0 :         slice_probs = new u8[size.Y];
     253           0 :         for (int y = 0; y != size.Y; y++)
     254           0 :                 slice_probs[y] = (version >= 3) ? readU8(ss) : MTSCHEM_PROB_ALWAYS_OLD;
     255             : 
     256             :         //// Read node names
     257           0 :         u16 nidmapcount = readU16(ss);
     258           0 :         for (int i = 0; i != nidmapcount; i++) {
     259           0 :                 std::string name = deSerializeString(ss);
     260             : 
     261             :                 // Instances of "ignore" from v1 are converted to air (and instances
     262             :                 // are fixed to have MTSCHEM_PROB_NEVER later on).
     263           0 :                 if (name == "ignore") {
     264           0 :                         name = "air";
     265           0 :                         cignore = i;
     266           0 :                         have_cignore = true;
     267             :                 }
     268             : 
     269           0 :                 names->push_back(name);
     270             :         }
     271             : 
     272             :         //// Read node data
     273           0 :         size_t nodecount = size.X * size.Y * size.Z;
     274             : 
     275           0 :         delete []schemdata;
     276           0 :         schemdata = new MapNode[nodecount];
     277             : 
     278           0 :         MapNode::deSerializeBulk(ss, SER_FMT_VER_HIGHEST_READ, schemdata,
     279           0 :                 nodecount, 2, 2, true);
     280             : 
     281             :         // Fix probability values for nodes that were ignore; removed in v2
     282           0 :         if (version < 2) {
     283           0 :                 for (size_t i = 0; i != nodecount; i++) {
     284           0 :                         if (schemdata[i].param1 == 0)
     285           0 :                                 schemdata[i].param1 = MTSCHEM_PROB_ALWAYS_OLD;
     286           0 :                         if (have_cignore && schemdata[i].getContent() == cignore)
     287           0 :                                 schemdata[i].param1 = MTSCHEM_PROB_NEVER;
     288             :                 }
     289             :         }
     290             : 
     291             :         // Fix probability values for probability range truncation introduced in v4
     292           0 :         if (version < 4) {
     293           0 :                 for (s16 y = 0; y != size.Y; y++)
     294           0 :                         slice_probs[y] >>= 1;
     295           0 :                 for (size_t i = 0; i != nodecount; i++)
     296           0 :                         schemdata[i].param1 >>= 1;
     297             :         }
     298             : 
     299           0 :         return true;
     300             : }
     301             : 
     302             : 
     303           0 : bool Schematic::serializeToMts(std::ostream *os,
     304             :         const std::vector<std::string> &names)
     305             : {
     306           0 :         std::ostream &ss = *os;
     307             : 
     308           0 :         writeU32(ss, MTSCHEM_FILE_SIGNATURE);         // signature
     309           0 :         writeU16(ss, MTSCHEM_FILE_VER_HIGHEST_WRITE); // version
     310           0 :         writeV3S16(ss, size);                         // schematic size
     311             : 
     312           0 :         for (int y = 0; y != size.Y; y++)             // Y slice probabilities
     313           0 :                 writeU8(ss, slice_probs[y]);
     314             : 
     315           0 :         writeU16(ss, names.size()); // name count
     316           0 :         for (size_t i = 0; i != names.size(); i++)
     317           0 :                 ss << serializeString(names[i]); // node names
     318             : 
     319             :         // compressed bulk node data
     320           0 :         MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE,
     321           0 :                 schemdata, size.X * size.Y * size.Z, 2, 2, true);
     322             : 
     323           0 :         return true;
     324             : }
     325             : 
     326             : 
     327           0 : bool Schematic::serializeToLua(std::ostream *os,
     328             :         const std::vector<std::string> &names, bool use_comments, u32 indent_spaces)
     329             : {
     330           0 :         std::ostream &ss = *os;
     331             : 
     332           0 :         std::string indent("\t");
     333           0 :         if (indent_spaces > 0)
     334           0 :                 indent.assign(indent_spaces, ' ');
     335             : 
     336             :         //// Write header
     337             :         {
     338           0 :                 ss << "schematic = {" << std::endl;
     339           0 :                 ss << indent << "size = "
     340           0 :                         << "{x=" << size.X
     341           0 :                         << ", y=" << size.Y
     342           0 :                         << ", z=" << size.Z
     343           0 :                         << "}," << std::endl;
     344             :         }
     345             : 
     346             :         //// Write y-slice probabilities
     347             :         {
     348           0 :                 ss << indent << "yslice_prob = {" << std::endl;
     349             : 
     350           0 :                 for (u16 y = 0; y != size.Y; y++) {
     351           0 :                         u8 probability = slice_probs[y] & MTSCHEM_PROB_MASK;
     352             : 
     353           0 :                         ss << indent << indent << "{"
     354           0 :                                 << "ypos=" << y
     355           0 :                                 << ", prob=" << (u16)probability * 2
     356           0 :                                 << "}," << std::endl;
     357             :                 }
     358             : 
     359           0 :                 ss << indent << "}," << std::endl;
     360             :         }
     361             : 
     362             :         //// Write node data
     363             :         {
     364           0 :                 ss << indent << "data = {" << std::endl;
     365             : 
     366           0 :                 u32 i = 0;
     367           0 :                 for (u16 z = 0; z != size.Z; z++)
     368           0 :                 for (u16 y = 0; y != size.Y; y++) {
     369           0 :                         if (use_comments) {
     370           0 :                                 ss << std::endl
     371           0 :                                         << indent << indent
     372           0 :                                         << "-- z=" << z
     373           0 :                                         << ", y=" << y << std::endl;
     374             :                         }
     375             : 
     376           0 :                         for (u16 x = 0; x != size.X; x++, i++) {
     377           0 :                                 u8 probability   = schemdata[i].param1 & MTSCHEM_PROB_MASK;
     378           0 :                                 bool force_place = schemdata[i].param1 & MTSCHEM_FORCE_PLACE;
     379             : 
     380           0 :                                 ss << indent << indent << "{"
     381           0 :                                         << "name=\"" << names[schemdata[i].getContent()]
     382           0 :                                         << "\", prob=" << (u16)probability * 2
     383           0 :                                         << ", param2=" << (u16)schemdata[i].param2;
     384             : 
     385           0 :                                 if (force_place)
     386           0 :                                         ss << ", force_place=true";
     387             : 
     388           0 :                                 ss << "}," << std::endl;
     389             :                         }
     390             :                 }
     391             : 
     392           0 :                 ss << indent << "}," << std::endl;
     393             :         }
     394             : 
     395           0 :         ss << "}" << std::endl;
     396             : 
     397           0 :         return true;
     398             : }
     399             : 
     400             : 
     401           0 : bool Schematic::loadSchematicFromFile(const std::string &filename,
     402             :         INodeDefManager *ndef, StringMap *replace_names)
     403             : {
     404           0 :         std::ifstream is(filename.c_str(), std::ios_base::binary);
     405           0 :         if (!is.good()) {
     406           0 :                 errorstream << "Schematic::loadSchematicFile: unable to open file '"
     407           0 :                         << filename << "'" << std::endl;
     408           0 :                 return false;
     409             :         }
     410             : 
     411           0 :         size_t origsize = m_nodenames.size();
     412           0 :         if (!deserializeFromMts(&is, &m_nodenames))
     413           0 :                 return false;
     414             : 
     415           0 :         if (replace_names) {
     416           0 :                 for (size_t i = origsize; i != m_nodenames.size(); i++) {
     417           0 :                         std::string &name = m_nodenames[i];
     418           0 :                         StringMap::iterator it = replace_names->find(name);
     419           0 :                         if (it != replace_names->end())
     420           0 :                                 name = it->second;
     421             :                 }
     422             :         }
     423             : 
     424           0 :         m_nnlistsizes.push_back(m_nodenames.size() - origsize);
     425             : 
     426           0 :         if (ndef)
     427           0 :                 ndef->pendNodeResolve(this);
     428             : 
     429           0 :         return true;
     430             : }
     431             : 
     432             : 
     433           0 : bool Schematic::saveSchematicToFile(const std::string &filename,
     434             :         INodeDefManager *ndef)
     435             : {
     436           0 :         MapNode *orig_schemdata = schemdata;
     437           0 :         std::vector<std::string> ndef_nodenames;
     438             :         std::vector<std::string> *names;
     439             : 
     440           0 :         if (m_resolve_done && ndef == NULL)
     441           0 :                 ndef = m_ndef;
     442             : 
     443           0 :         if (ndef) {
     444           0 :                 names = &ndef_nodenames;
     445             : 
     446           0 :                 u32 volume = size.X * size.Y * size.Z;
     447           0 :                 schemdata = new MapNode[volume];
     448           0 :                 for (u32 i = 0; i != volume; i++)
     449           0 :                         schemdata[i] = orig_schemdata[i];
     450             : 
     451           0 :                 generate_nodelist_and_update_ids(schemdata, volume, names, ndef);
     452             :         } else { // otherwise, use the names we have on hand in the list
     453           0 :                 names = &m_nodenames;
     454             :         }
     455             : 
     456           0 :         std::ostringstream os(std::ios_base::binary);
     457           0 :         bool status = serializeToMts(&os, *names);
     458             : 
     459           0 :         if (ndef) {
     460           0 :                 delete []schemdata;
     461           0 :                 schemdata = orig_schemdata;
     462             :         }
     463             : 
     464           0 :         if (!status)
     465           0 :                 return false;
     466             : 
     467           0 :         return fs::safeWriteToFile(filename, os.str());
     468             : }
     469             : 
     470             : 
     471           0 : bool Schematic::getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2)
     472             : {
     473           0 :         MMVManip *vm = new MMVManip(map);
     474             : 
     475           0 :         v3s16 bp1 = getNodeBlockPos(p1);
     476           0 :         v3s16 bp2 = getNodeBlockPos(p2);
     477           0 :         vm->initialEmerge(bp1, bp2);
     478             : 
     479           0 :         size = p2 - p1 + 1;
     480             : 
     481           0 :         slice_probs = new u8[size.Y];
     482           0 :         for (s16 y = 0; y != size.Y; y++)
     483           0 :                 slice_probs[y] = MTSCHEM_PROB_ALWAYS;
     484             : 
     485           0 :         schemdata = new MapNode[size.X * size.Y * size.Z];
     486             : 
     487           0 :         u32 i = 0;
     488           0 :         for (s16 z = p1.Z; z <= p2.Z; z++)
     489           0 :         for (s16 y = p1.Y; y <= p2.Y; y++) {
     490           0 :                 u32 vi = vm->m_area.index(p1.X, y, z);
     491           0 :                 for (s16 x = p1.X; x <= p2.X; x++, i++, vi++) {
     492           0 :                         schemdata[i] = vm->m_data[vi];
     493           0 :                         schemdata[i].param1 = MTSCHEM_PROB_ALWAYS;
     494             :                 }
     495             :         }
     496             : 
     497           0 :         delete vm;
     498           0 :         return true;
     499             : }
     500             : 
     501             : 
     502           0 : void Schematic::applyProbabilities(v3s16 p0,
     503             :         std::vector<std::pair<v3s16, u8> > *plist,
     504             :         std::vector<std::pair<s16, u8> > *splist)
     505             : {
     506           0 :         for (size_t i = 0; i != plist->size(); i++) {
     507           0 :                 v3s16 p = (*plist)[i].first - p0;
     508           0 :                 int index = p.Z * (size.Y * size.X) + p.Y * size.X + p.X;
     509           0 :                 if (index < size.Z * size.Y * size.X) {
     510           0 :                         u8 prob = (*plist)[i].second;
     511           0 :                         schemdata[index].param1 = prob;
     512             : 
     513             :                         // trim unnecessary node names from schematic
     514           0 :                         if (prob == MTSCHEM_PROB_NEVER)
     515           0 :                                 schemdata[index].setContent(CONTENT_AIR);
     516             :                 }
     517             :         }
     518             : 
     519           0 :         for (size_t i = 0; i != splist->size(); i++) {
     520           0 :                 s16 y = (*splist)[i].first - p0.Y;
     521           0 :                 slice_probs[y] = (*splist)[i].second;
     522             :         }
     523           0 : }
     524             : 
     525             : 
     526           0 : void generate_nodelist_and_update_ids(MapNode *nodes, size_t nodecount,
     527             :         std::vector<std::string> *usednodes, INodeDefManager *ndef)
     528             : {
     529           0 :         std::map<content_t, content_t> nodeidmap;
     530           0 :         content_t numids = 0;
     531             : 
     532           0 :         for (size_t i = 0; i != nodecount; i++) {
     533             :                 content_t id;
     534           0 :                 content_t c = nodes[i].getContent();
     535             : 
     536           0 :                 std::map<content_t, content_t>::const_iterator it = nodeidmap.find(c);
     537           0 :                 if (it == nodeidmap.end()) {
     538           0 :                         id = numids;
     539           0 :                         numids++;
     540             : 
     541           0 :                         usednodes->push_back(ndef->get(c).name);
     542           0 :                         nodeidmap.insert(std::make_pair(c, id));
     543             :                 } else {
     544           0 :                         id = it->second;
     545             :                 }
     546           0 :                 nodes[i].setContent(id);
     547             :         }
     548           3 : }

Generated by: LCOV version 1.11