3 Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
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.
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.
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.
21 #include "mg_schematic.h"
28 #include "util/numeric.h"
29 #include "util/serialize.h"
30 #include "serialization.h"
33 ///////////////////////////////////////////////////////////////////////////////
36 SchematicManager::SchematicManager(IGameDef *gamedef) :
37 ObjDefManager(gamedef, OBJDEF_SCHEMATIC)
43 void SchematicManager::clear()
45 EmergeManager *emerge = m_gamedef->getEmergeManager();
47 // Remove all dangling references in Decorations
48 DecorationManager *decomgr = emerge->decomgr;
49 for (size_t i = 0; i != decomgr->getNumObjects(); i++) {
50 Decoration *deco = (Decoration *)decomgr->getRaw(i);
53 DecoSchematic *dschem = dynamic_cast<DecoSchematic *>(deco);
55 dschem->schematic = NULL;
56 } catch(std::bad_cast) {
60 ObjDefManager::clear();
64 ///////////////////////////////////////////////////////////////////////////////
67 Schematic::Schematic()
72 size = v3s16(0, 0, 0);
76 Schematic::~Schematic()
83 void Schematic::resolveNodeNames()
85 getIdsFromNrBacklog(&c_nodes, true, CONTENT_AIR);
87 size_t bufsize = size.X * size.Y * size.Z;
88 for (size_t i = 0; i != bufsize; i++) {
89 content_t c_original = schemdata[i].getContent();
90 content_t c_new = c_nodes[c_original];
91 schemdata[i].setContent(c_new);
96 void Schematic::blitToVManip(v3s16 p, MMVManip *vm, Rotation rot,
97 bool force_placement, INodeDefManager *ndef)
100 int ystride = size.X;
101 int zstride = size.X * size.Y;
107 int i_start, i_step_x, i_step_z;
116 i_start = zstride * (sz - 1) + sx - 1;
121 i_start = zstride * (sz - 1);
133 for (s16 y = 0; y != sy; y++) {
134 if (slice_probs[y] != MTSCHEM_PROB_ALWAYS &&
135 myrand_range(1, 255) > slice_probs[y])
138 for (s16 z = 0; z != sz; z++) {
139 u32 i = z * i_step_z + y * ystride + i_start;
140 for (s16 x = 0; x != sx; x++, i += i_step_x) {
141 u32 vi = vm->m_area.index(p.X + x, y_map, p.Z + z);
142 if (!vm->m_area.contains(vi))
145 if (schemdata[i].getContent() == CONTENT_IGNORE)
148 if (schemdata[i].param1 == MTSCHEM_PROB_NEVER)
151 if (!force_placement) {
152 content_t c = vm->m_data[vi].getContent();
153 if (c != CONTENT_AIR && c != CONTENT_IGNORE)
157 if (schemdata[i].param1 != MTSCHEM_PROB_ALWAYS &&
158 myrand_range(1, 255) > schemdata[i].param1)
161 vm->m_data[vi] = schemdata[i];
162 vm->m_data[vi].param1 = 0;
165 vm->m_data[vi].rotateAlongYAxis(ndef, rot);
173 void Schematic::placeStructure(Map *map, v3s16 p, u32 flags, Rotation rot,
174 bool force_placement, INodeDefManager *ndef)
176 assert(schemdata != NULL); // Pre-condition
177 MMVManip *vm = new MMVManip(map);
179 if (rot == ROTATE_RAND)
180 rot = (Rotation)myrand_range(ROTATE_0, ROTATE_270);
182 v3s16 s = (rot == ROTATE_90 || rot == ROTATE_270) ?
183 v3s16(size.Z, size.Y, size.X) : size;
185 if (flags & DECO_PLACE_CENTER_X)
186 p.X -= (s.X + 1) / 2;
187 if (flags & DECO_PLACE_CENTER_Y)
188 p.Y -= (s.Y + 1) / 2;
189 if (flags & DECO_PLACE_CENTER_Z)
190 p.Z -= (s.Z + 1) / 2;
192 v3s16 bp1 = getNodeBlockPos(p);
193 v3s16 bp2 = getNodeBlockPos(p + s - v3s16(1,1,1));
194 vm->initialEmerge(bp1, bp2);
196 blitToVManip(p, vm, rot, force_placement, ndef);
198 std::map<v3s16, MapBlock *> lighting_modified_blocks;
199 std::map<v3s16, MapBlock *> modified_blocks;
200 vm->blitBackAll(&modified_blocks);
202 // TODO: Optimize this by using Mapgen::calcLighting() instead
203 lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
204 map->updateLighting(lighting_modified_blocks, modified_blocks);
207 event.type = MEET_OTHER;
208 for (std::map<v3s16, MapBlock *>::iterator
209 it = modified_blocks.begin();
210 it != modified_blocks.end(); ++it)
211 event.modified_blocks.insert(it->first);
213 map->dispatchEvent(&event);
217 bool Schematic::deserializeFromMts(std::istream *is, std::vector<std::string> *names)
219 std::istream &ss = *is;
220 content_t cignore = CONTENT_IGNORE;
221 bool have_cignore = false;
223 u32 signature = readU32(ss);
224 if (signature != MTSCHEM_FILE_SIGNATURE) {
225 errorstream << "Schematic::deserializeFromMts: invalid schematic "
230 u16 version = readU16(ss);
231 if (version > MTSCHEM_FILE_VER_HIGHEST_READ) {
232 errorstream << "Schematic::deserializeFromMts: unsupported schematic "
233 "file version" << std::endl;
237 size = readV3S16(ss);
239 delete []slice_probs;
240 slice_probs = new u8[size.Y];
241 for (int y = 0; y != size.Y; y++)
242 slice_probs[y] = (version >= 3) ? readU8(ss) : MTSCHEM_PROB_ALWAYS;
244 u16 nidmapcount = readU16(ss);
245 for (int i = 0; i != nidmapcount; i++) {
246 std::string name = deSerializeString(ss);
248 // Instances of "ignore" from ver 1 are converted to air (and instances
249 // are fixed to have MTSCHEM_PROB_NEVER later on).
250 if (name == "ignore") {
256 names->push_back(name);
259 size_t nodecount = size.X * size.Y * size.Z;
262 schemdata = new MapNode[nodecount];
264 MapNode::deSerializeBulk(ss, SER_FMT_VER_HIGHEST_READ, schemdata,
265 nodecount, 2, 2, true);
267 // fix any probability values for nodes that were ignore
269 for (size_t i = 0; i != nodecount; i++) {
270 if (schemdata[i].param1 == 0)
271 schemdata[i].param1 = MTSCHEM_PROB_ALWAYS;
272 if (have_cignore && schemdata[i].getContent() == cignore)
273 schemdata[i].param1 = MTSCHEM_PROB_NEVER;
281 bool Schematic::serializeToMts(std::ostream *os)
283 std::ostream &ss = *os;
285 writeU32(ss, MTSCHEM_FILE_SIGNATURE); // signature
286 writeU16(ss, MTSCHEM_FILE_VER_HIGHEST_WRITE); // version
287 writeV3S16(ss, size); // schematic size
289 for (int y = 0; y != size.Y; y++) // Y slice probabilities
290 writeU8(ss, slice_probs[y]);
292 std::vector<content_t> usednodes;
293 int nodecount = size.X * size.Y * size.Z;
294 build_nnlist_and_update_ids(schemdata, nodecount, &usednodes);
296 u16 numids = usednodes.size();
297 writeU16(ss, numids); // name count
298 for (int i = 0; i != numids; i++)
299 ss << serializeString(getNodeName(usednodes[i])); // node names
301 // compressed bulk node data
302 MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE,
303 schemdata, nodecount, 2, 2, true);
309 bool Schematic::serializeToLua(std::ostream *os, bool use_comments)
311 std::ostream &ss = *os;
315 ss << "schematic = {" << std::endl;
320 << "}," << std::endl;
323 //// Write y-slice probabilities
325 ss << "\tyslice_prob = {" << std::endl;
327 for (u16 y = 0; y != size.Y; y++) {
330 << ", prob=" << (u16)slice_probs[y]
331 << "}," << std::endl;
334 ss << "\t}," << std::endl;
339 ss << "\tdata = {" << std::endl;
342 for (u16 z = 0; z != size.Z; z++)
343 for (u16 y = 0; y != size.Y; y++) {
347 << ", y=" << y << std::endl;
350 for (u16 x = 0; x != size.X; x++, i++) {
352 << "name=\"" << getNodeName(schemdata[i].getContent())
353 << "\", param1=" << (u16)schemdata[i].param1
354 << ", param2=" << (u16)schemdata[i].param2
355 << "}," << std::endl;
359 ss << "\t}," << std::endl;
362 ss << "}" << std::endl;
368 bool Schematic::loadSchematicFromFile(const std::string &filename,
369 INodeDefManager *ndef, StringMap *replace_names,
370 NodeResolveMethod resolve_method)
372 std::ifstream is(filename.c_str(), std::ios_base::binary);
374 errorstream << "Schematic::loadSchematicFile: unable to open file '"
375 << filename << "'" << std::endl;
379 size_t origsize = m_nodenames.size();
380 if (!deserializeFromMts(&is, &m_nodenames))
384 for (size_t i = origsize; i != m_nodenames.size(); i++) {
385 std::string &name = m_nodenames[i];
386 StringMap::iterator it = replace_names->find(name);
387 if (it != replace_names->end())
392 m_nnlistsizes.push_back(m_nodenames.size() - origsize);
394 ndef->pendNodeResolve(this, resolve_method);
400 bool Schematic::saveSchematicToFile(const std::string &filename)
402 std::ostringstream os(std::ios_base::binary);
404 return fs::safeWriteToFile(filename, os.str());
408 bool Schematic::getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2)
410 MMVManip *vm = new MMVManip(map);
412 v3s16 bp1 = getNodeBlockPos(p1);
413 v3s16 bp2 = getNodeBlockPos(p2);
414 vm->initialEmerge(bp1, bp2);
418 slice_probs = new u8[size.Y];
419 for (s16 y = 0; y != size.Y; y++)
420 slice_probs[y] = MTSCHEM_PROB_ALWAYS;
422 schemdata = new MapNode[size.X * size.Y * size.Z];
425 for (s16 z = p1.Z; z <= p2.Z; z++)
426 for (s16 y = p1.Y; y <= p2.Y; y++) {
427 u32 vi = vm->m_area.index(p1.X, y, z);
428 for (s16 x = p1.X; x <= p2.X; x++, i++, vi++) {
429 schemdata[i] = vm->m_data[vi];
430 schemdata[i].param1 = MTSCHEM_PROB_ALWAYS;
439 void Schematic::applyProbabilities(v3s16 p0,
440 std::vector<std::pair<v3s16, u8> > *plist,
441 std::vector<std::pair<s16, u8> > *splist)
443 for (size_t i = 0; i != plist->size(); i++) {
444 v3s16 p = (*plist)[i].first - p0;
445 int index = p.Z * (size.Y * size.X) + p.Y * size.X + p.X;
446 if (index < size.Z * size.Y * size.X) {
447 u8 prob = (*plist)[i].second;
448 schemdata[index].param1 = prob;
450 // trim unnecessary node names from schematic
451 if (prob == MTSCHEM_PROB_NEVER)
452 schemdata[index].setContent(CONTENT_AIR);
456 for (size_t i = 0; i != splist->size(); i++) {
457 s16 y = (*splist)[i].first - p0.Y;
458 slice_probs[y] = (*splist)[i].second;
463 void build_nnlist_and_update_ids(MapNode *nodes, u32 nodecount,
464 std::vector<content_t> *usednodes)
466 std::map<content_t, content_t> nodeidmap;
467 content_t numids = 0;
469 for (u32 i = 0; i != nodecount; i++) {
471 content_t c = nodes[i].getContent();
473 std::map<content_t, content_t>::const_iterator it = nodeidmap.find(c);
474 if (it == nodeidmap.end()) {
478 usednodes->push_back(c);
479 nodeidmap.insert(std::make_pair(c, id));
483 nodes[i].setContent(id);