]> git.lizzy.rs Git - dragonfireclient.git/blob - src/nodedef.cpp
Improve lighting of entities.
[dragonfireclient.git] / src / nodedef.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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 "nodedef.h"
21
22 #include "itemdef.h"
23 #ifndef SERVER
24 #include "client/mesh.h"
25 #include "client/shader.h"
26 #include "client/client.h"
27 #include "client/renderingengine.h"
28 #include "client/tile.h"
29 #include <IMeshManipulator.h>
30 #endif
31 #include "log.h"
32 #include "settings.h"
33 #include "nameidmapping.h"
34 #include "util/numeric.h"
35 #include "util/serialize.h"
36 #include "exceptions.h"
37 #include "debug.h"
38 #include "gamedef.h"
39 #include "mapnode.h"
40 #include <fstream> // Used in applyTextureOverrides()
41 #include <algorithm>
42 #include <cmath>
43
44 /*
45         NodeBox
46 */
47
48 void NodeBox::reset()
49 {
50         type = NODEBOX_REGULAR;
51         // default is empty
52         fixed.clear();
53         // default is sign/ladder-like
54         wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
55         wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
56         wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
57         // no default for other parts
58         connect_top.clear();
59         connect_bottom.clear();
60         connect_front.clear();
61         connect_left.clear();
62         connect_back.clear();
63         connect_right.clear();
64         disconnected_top.clear();
65         disconnected_bottom.clear();
66         disconnected_front.clear();
67         disconnected_left.clear();
68         disconnected_back.clear();
69         disconnected_right.clear();
70         disconnected.clear();
71         disconnected_sides.clear();
72 }
73
74 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
75 {
76         // Protocol >= 36
77         const u8 version = 6;
78         writeU8(os, version);
79
80         switch (type) {
81         case NODEBOX_LEVELED:
82         case NODEBOX_FIXED:
83                 writeU8(os, type);
84
85                 writeU16(os, fixed.size());
86                 for (const aabb3f &nodebox : fixed) {
87                         writeV3F32(os, nodebox.MinEdge);
88                         writeV3F32(os, nodebox.MaxEdge);
89                 }
90                 break;
91         case NODEBOX_WALLMOUNTED:
92                 writeU8(os, type);
93
94                 writeV3F32(os, wall_top.MinEdge);
95                 writeV3F32(os, wall_top.MaxEdge);
96                 writeV3F32(os, wall_bottom.MinEdge);
97                 writeV3F32(os, wall_bottom.MaxEdge);
98                 writeV3F32(os, wall_side.MinEdge);
99                 writeV3F32(os, wall_side.MaxEdge);
100                 break;
101         case NODEBOX_CONNECTED:
102                 writeU8(os, type);
103
104 #define WRITEBOX(box) \
105                 writeU16(os, (box).size()); \
106                 for (const aabb3f &i: (box)) { \
107                         writeV3F32(os, i.MinEdge); \
108                         writeV3F32(os, i.MaxEdge); \
109                 };
110
111                 WRITEBOX(fixed);
112                 WRITEBOX(connect_top);
113                 WRITEBOX(connect_bottom);
114                 WRITEBOX(connect_front);
115                 WRITEBOX(connect_left);
116                 WRITEBOX(connect_back);
117                 WRITEBOX(connect_right);
118                 WRITEBOX(disconnected_top);
119                 WRITEBOX(disconnected_bottom);
120                 WRITEBOX(disconnected_front);
121                 WRITEBOX(disconnected_left);
122                 WRITEBOX(disconnected_back);
123                 WRITEBOX(disconnected_right);
124                 WRITEBOX(disconnected);
125                 WRITEBOX(disconnected_sides);
126                 break;
127         default:
128                 writeU8(os, type);
129                 break;
130         }
131 }
132
133 void NodeBox::deSerialize(std::istream &is)
134 {
135         int version = readU8(is);
136         if (version < 6)
137                 throw SerializationError("unsupported NodeBox version");
138
139         reset();
140
141         type = (enum NodeBoxType)readU8(is);
142
143         if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
144         {
145                 u16 fixed_count = readU16(is);
146                 while(fixed_count--)
147                 {
148                         aabb3f box;
149                         box.MinEdge = readV3F32(is);
150                         box.MaxEdge = readV3F32(is);
151                         fixed.push_back(box);
152                 }
153         }
154         else if(type == NODEBOX_WALLMOUNTED)
155         {
156                 wall_top.MinEdge = readV3F32(is);
157                 wall_top.MaxEdge = readV3F32(is);
158                 wall_bottom.MinEdge = readV3F32(is);
159                 wall_bottom.MaxEdge = readV3F32(is);
160                 wall_side.MinEdge = readV3F32(is);
161                 wall_side.MaxEdge = readV3F32(is);
162         }
163         else if (type == NODEBOX_CONNECTED)
164         {
165 #define READBOXES(box) { \
166                 count = readU16(is); \
167                 (box).reserve(count); \
168                 while (count--) { \
169                         v3f min = readV3F32(is); \
170                         v3f max = readV3F32(is); \
171                         (box).emplace_back(min, max); }; }
172
173                 u16 count;
174
175                 READBOXES(fixed);
176                 READBOXES(connect_top);
177                 READBOXES(connect_bottom);
178                 READBOXES(connect_front);
179                 READBOXES(connect_left);
180                 READBOXES(connect_back);
181                 READBOXES(connect_right);
182                 READBOXES(disconnected_top);
183                 READBOXES(disconnected_bottom);
184                 READBOXES(disconnected_front);
185                 READBOXES(disconnected_left);
186                 READBOXES(disconnected_back);
187                 READBOXES(disconnected_right);
188                 READBOXES(disconnected);
189                 READBOXES(disconnected_sides);
190         }
191 }
192
193 /*
194         TileDef
195 */
196
197 #define TILE_FLAG_BACKFACE_CULLING      (1 << 0)
198 #define TILE_FLAG_TILEABLE_HORIZONTAL   (1 << 1)
199 #define TILE_FLAG_TILEABLE_VERTICAL     (1 << 2)
200 #define TILE_FLAG_HAS_COLOR     (1 << 3)
201 #define TILE_FLAG_HAS_SCALE     (1 << 4)
202 #define TILE_FLAG_HAS_ALIGN_STYLE       (1 << 5)
203
204 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
205 {
206         // protocol_version >= 36
207         u8 version = 6;
208         writeU8(os, version);
209
210         if (protocol_version > 39) {
211                 os << serializeString16(name);
212         } else {
213                 // Before f018737, TextureSource::getTextureAverageColor did not handle
214                 // missing textures. "[png" can be used as base texture, but is not known
215                 // on older clients. Hence use "blank.png" to avoid this problem.
216                 if (!name.empty() && name[0] == '[')
217                         os << serializeString16("blank.png^" + name);
218                 else
219                         os << serializeString16(name);
220         }
221         animation.serialize(os, version);
222         bool has_scale = scale > 0;
223         u16 flags = 0;
224         if (backface_culling)
225                 flags |= TILE_FLAG_BACKFACE_CULLING;
226         if (tileable_horizontal)
227                 flags |= TILE_FLAG_TILEABLE_HORIZONTAL;
228         if (tileable_vertical)
229                 flags |= TILE_FLAG_TILEABLE_VERTICAL;
230         if (has_color)
231                 flags |= TILE_FLAG_HAS_COLOR;
232         if (has_scale)
233                 flags |= TILE_FLAG_HAS_SCALE;
234         if (align_style != ALIGN_STYLE_NODE)
235                 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
236         writeU16(os, flags);
237         if (has_color) {
238                 writeU8(os, color.getRed());
239                 writeU8(os, color.getGreen());
240                 writeU8(os, color.getBlue());
241         }
242         if (has_scale)
243                 writeU8(os, scale);
244         if (align_style != ALIGN_STYLE_NODE)
245                 writeU8(os, align_style);
246 }
247
248 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
249         NodeDrawType drawtype)
250 {
251         int version = readU8(is);
252         if (version < 6)
253                 throw SerializationError("unsupported TileDef version");
254         name = deSerializeString16(is);
255         animation.deSerialize(is, version);
256         u16 flags = readU16(is);
257         backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
258         tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL;
259         tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL;
260         has_color = flags & TILE_FLAG_HAS_COLOR;
261         bool has_scale = flags & TILE_FLAG_HAS_SCALE;
262         bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE;
263         if (has_color) {
264                 color.setRed(readU8(is));
265                 color.setGreen(readU8(is));
266                 color.setBlue(readU8(is));
267         }
268         scale = has_scale ? readU8(is) : 0;
269         if (has_align_style)
270                 align_style = static_cast<AlignStyle>(readU8(is));
271         else
272                 align_style = ALIGN_STYLE_NODE;
273 }
274
275 void TextureSettings::readSettings()
276 {
277         connected_glass                = g_settings->getBool("connected_glass");
278         opaque_water                   = g_settings->getBool("opaque_water");
279         bool smooth_lighting           = g_settings->getBool("smooth_lighting");
280         enable_mesh_cache              = g_settings->getBool("enable_mesh_cache");
281         enable_minimap                 = g_settings->getBool("enable_minimap");
282         node_texture_size              = g_settings->getU16("texture_min_size");
283         std::string leaves_style_str   = g_settings->get("leaves_style");
284         std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
285         std::string autoscale_mode_str = g_settings->get("autoscale_mode");
286
287         // Mesh cache is not supported in combination with smooth lighting
288         if (smooth_lighting)
289                 enable_mesh_cache = false;
290
291         if (leaves_style_str == "fancy") {
292                 leaves_style = LEAVES_FANCY;
293         } else if (leaves_style_str == "simple") {
294                 leaves_style = LEAVES_SIMPLE;
295         } else {
296                 leaves_style = LEAVES_OPAQUE;
297         }
298
299         if (world_aligned_mode_str == "enable")
300                 world_aligned_mode = WORLDALIGN_ENABLE;
301         else if (world_aligned_mode_str == "force_solid")
302                 world_aligned_mode = WORLDALIGN_FORCE;
303         else if (world_aligned_mode_str == "force_nodebox")
304                 world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
305         else
306                 world_aligned_mode = WORLDALIGN_DISABLE;
307
308         if (autoscale_mode_str == "enable")
309                 autoscale_mode = AUTOSCALE_ENABLE;
310         else if (autoscale_mode_str == "force")
311                 autoscale_mode = AUTOSCALE_FORCE;
312         else
313                 autoscale_mode = AUTOSCALE_DISABLE;
314 }
315
316 /*
317         ContentFeatures
318 */
319
320 ContentFeatures::ContentFeatures()
321 {
322         reset();
323 }
324
325 ContentFeatures::~ContentFeatures()
326 {
327 #ifndef SERVER
328         for (u16 j = 0; j < 6; j++) {
329                 delete tiles[j].layers[0].frames;
330                 delete tiles[j].layers[1].frames;
331         }
332         for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
333                 delete special_tiles[j].layers[0].frames;
334 #endif
335 }
336
337 void ContentFeatures::reset()
338 {
339         /*
340                 Cached stuff
341         */
342 #ifndef SERVER
343         solidness = 2;
344         visual_solidness = 0;
345         backface_culling = true;
346
347 #endif
348         has_on_construct = false;
349         has_on_destruct = false;
350         has_after_destruct = false;
351         /*
352                 Actual data
353
354                 NOTE: Most of this is always overridden by the default values given
355                       in builtin.lua
356         */
357         name = "";
358         groups.clear();
359         // Unknown nodes can be dug
360         groups["dig_immediate"] = 2;
361         drawtype = NDT_NORMAL;
362         mesh = "";
363 #ifndef SERVER
364         for (auto &i : mesh_ptr)
365                 i = NULL;
366         minimap_color = video::SColor(0, 0, 0, 0);
367 #endif
368         visual_scale = 1.0;
369         for (auto &i : tiledef)
370                 i = TileDef();
371         for (auto &j : tiledef_special)
372                 j = TileDef();
373         alpha = ALPHAMODE_OPAQUE;
374         post_effect_color = video::SColor(0, 0, 0, 0);
375         param_type = CPT_NONE;
376         param_type_2 = CPT2_NONE;
377         is_ground_content = false;
378         light_propagates = false;
379         sunlight_propagates = false;
380         walkable = true;
381         pointable = true;
382         diggable = true;
383         climbable = false;
384         buildable_to = false;
385         floodable = false;
386         rightclickable = true;
387         leveled = 0;
388         leveled_max = LEVELED_MAX;
389         liquid_type = LIQUID_NONE;
390         liquid_alternative_flowing = "";
391         liquid_alternative_flowing_id = CONTENT_IGNORE;
392         liquid_alternative_source = "";
393         liquid_alternative_source_id = CONTENT_IGNORE;
394         liquid_viscosity = 0;
395         liquid_renewable = true;
396         liquid_range = LIQUID_LEVEL_MAX+1;
397         drowning = 0;
398         light_source = 0;
399         damage_per_second = 0;
400         node_box = NodeBox();
401         selection_box = NodeBox();
402         collision_box = NodeBox();
403         waving = 0;
404         legacy_facedir_simple = false;
405         legacy_wallmounted = false;
406         sound_footstep = SimpleSoundSpec();
407         sound_dig = SimpleSoundSpec("__group");
408         sound_dug = SimpleSoundSpec();
409         connects_to.clear();
410         connects_to_ids.clear();
411         connect_sides = 0;
412         color = video::SColor(0xFFFFFFFF);
413         palette_name = "";
414         palette = NULL;
415         node_dig_prediction = "air";
416         move_resistance = 0;
417         liquid_move_physics = false;
418 }
419
420 void ContentFeatures::setAlphaFromLegacy(u8 legacy_alpha)
421 {
422         // No special handling for nodebox/mesh here as it doesn't make sense to
423         // throw warnings when the server is too old to support the "correct" way
424         switch (drawtype) {
425         case NDT_NORMAL:
426                 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_CLIP;
427                 break;
428         case NDT_LIQUID:
429         case NDT_FLOWINGLIQUID:
430                 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_BLEND;
431                 break;
432         default:
433                 alpha = legacy_alpha == 255 ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
434                 break;
435         }
436 }
437
438 u8 ContentFeatures::getAlphaForLegacy() const
439 {
440         // This is so simple only because 255 and 0 mean wildly different things
441         // depending on drawtype...
442         return alpha == ALPHAMODE_OPAQUE ? 255 : 0;
443 }
444
445 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
446 {
447         const u8 version = CONTENTFEATURES_VERSION;
448         writeU8(os, version);
449
450         // general
451         os << serializeString16(name);
452         writeU16(os, groups.size());
453         for (const auto &group : groups) {
454                 os << serializeString16(group.first);
455                 if (protocol_version < 41 && group.first.compare("bouncy") == 0) {
456                         // Old clients may choke on negative bouncy value
457                         writeS16(os, abs(group.second));
458                 } else {
459                         writeS16(os, group.second);
460                 }
461         }
462         writeU8(os, param_type);
463         writeU8(os, param_type_2);
464
465         // visual
466         writeU8(os, drawtype);
467         os << serializeString16(mesh);
468         writeF32(os, visual_scale);
469         writeU8(os, 6);
470         for (const TileDef &td : tiledef)
471                 td.serialize(os, protocol_version);
472         for (const TileDef &td : tiledef_overlay)
473                 td.serialize(os, protocol_version);
474         writeU8(os, CF_SPECIAL_COUNT);
475         for (const TileDef &td : tiledef_special) {
476                 td.serialize(os, protocol_version);
477         }
478         writeU8(os, getAlphaForLegacy());
479         writeU8(os, color.getRed());
480         writeU8(os, color.getGreen());
481         writeU8(os, color.getBlue());
482         os << serializeString16(palette_name);
483         writeU8(os, waving);
484         writeU8(os, connect_sides);
485         writeU16(os, connects_to_ids.size());
486         for (u16 connects_to_id : connects_to_ids)
487                 writeU16(os, connects_to_id);
488         writeARGB8(os, post_effect_color);
489         writeU8(os, leveled);
490
491         // lighting
492         writeU8(os, light_propagates);
493         writeU8(os, sunlight_propagates);
494         writeU8(os, light_source);
495
496         // map generation
497         writeU8(os, is_ground_content);
498
499         // interaction
500         writeU8(os, walkable);
501         writeU8(os, pointable);
502         writeU8(os, diggable);
503         writeU8(os, climbable);
504         writeU8(os, buildable_to);
505         writeU8(os, rightclickable);
506         writeU32(os, damage_per_second);
507
508         // liquid
509         LiquidType liquid_type_bc = liquid_type;
510         if (protocol_version <= 39) {
511                 // Since commit 7f25823, liquid drawtypes can be used even with LIQUID_NONE
512                 // solution: force liquid type accordingly to accepted values
513                 if (drawtype == NDT_LIQUID)
514                         liquid_type_bc = LIQUID_SOURCE;
515                 else if (drawtype == NDT_FLOWINGLIQUID)
516                         liquid_type_bc = LIQUID_FLOWING;
517         }
518         writeU8(os, liquid_type_bc);
519         os << serializeString16(liquid_alternative_flowing);
520         os << serializeString16(liquid_alternative_source);
521         writeU8(os, liquid_viscosity);
522         writeU8(os, liquid_renewable);
523         writeU8(os, liquid_range);
524         writeU8(os, drowning);
525         writeU8(os, floodable);
526
527         // node boxes
528         node_box.serialize(os, protocol_version);
529         selection_box.serialize(os, protocol_version);
530         collision_box.serialize(os, protocol_version);
531
532         // sound
533         sound_footstep.serialize(os, version);
534         sound_dig.serialize(os, version);
535         sound_dug.serialize(os, version);
536
537         // legacy
538         writeU8(os, legacy_facedir_simple);
539         writeU8(os, legacy_wallmounted);
540
541         // new attributes
542         os << serializeString16(node_dig_prediction);
543         writeU8(os, leveled_max);
544         writeU8(os, alpha);
545         writeU8(os, move_resistance);
546         writeU8(os, liquid_move_physics);
547 }
548
549 void ContentFeatures::deSerialize(std::istream &is)
550 {
551         // version detection
552         const u8 version = readU8(is);
553         if (version < CONTENTFEATURES_VERSION)
554                 throw SerializationError("unsupported ContentFeatures version");
555
556         // general
557         name = deSerializeString16(is);
558         groups.clear();
559         u32 groups_size = readU16(is);
560         for (u32 i = 0; i < groups_size; i++) {
561                 std::string name = deSerializeString16(is);
562                 int value = readS16(is);
563                 groups[name] = value;
564         }
565         param_type = (enum ContentParamType) readU8(is);
566         param_type_2 = (enum ContentParamType2) readU8(is);
567
568         // visual
569         drawtype = (enum NodeDrawType) readU8(is);
570         mesh = deSerializeString16(is);
571         visual_scale = readF32(is);
572         if (readU8(is) != 6)
573                 throw SerializationError("unsupported tile count");
574         for (TileDef &td : tiledef)
575                 td.deSerialize(is, version, drawtype);
576         for (TileDef &td : tiledef_overlay)
577                 td.deSerialize(is, version, drawtype);
578         if (readU8(is) != CF_SPECIAL_COUNT)
579                 throw SerializationError("unsupported CF_SPECIAL_COUNT");
580         for (TileDef &td : tiledef_special)
581                 td.deSerialize(is, version, drawtype);
582         setAlphaFromLegacy(readU8(is));
583         color.setRed(readU8(is));
584         color.setGreen(readU8(is));
585         color.setBlue(readU8(is));
586         palette_name = deSerializeString16(is);
587         waving = readU8(is);
588         connect_sides = readU8(is);
589         u16 connects_to_size = readU16(is);
590         connects_to_ids.clear();
591         for (u16 i = 0; i < connects_to_size; i++)
592                 connects_to_ids.push_back(readU16(is));
593         post_effect_color = readARGB8(is);
594         leveled = readU8(is);
595
596         // lighting-related
597         light_propagates = readU8(is);
598         sunlight_propagates = readU8(is);
599         light_source = readU8(is);
600         light_source = MYMIN(light_source, LIGHT_MAX);
601
602         // map generation
603         is_ground_content = readU8(is);
604
605         // interaction
606         walkable = readU8(is);
607         pointable = readU8(is);
608         diggable = readU8(is);
609         climbable = readU8(is);
610         buildable_to = readU8(is);
611         rightclickable = readU8(is);
612         damage_per_second = readU32(is);
613
614         // liquid
615         liquid_type = (enum LiquidType) readU8(is);
616         liquid_move_physics = liquid_type != LIQUID_NONE;
617         liquid_alternative_flowing = deSerializeString16(is);
618         liquid_alternative_source = deSerializeString16(is);
619         liquid_viscosity = readU8(is);
620         move_resistance = liquid_viscosity; // set default move_resistance
621         liquid_renewable = readU8(is);
622         liquid_range = readU8(is);
623         drowning = readU8(is);
624         floodable = readU8(is);
625
626         // node boxes
627         node_box.deSerialize(is);
628         selection_box.deSerialize(is);
629         collision_box.deSerialize(is);
630
631         // sounds
632         sound_footstep.deSerialize(is, version);
633         sound_dig.deSerialize(is, version);
634         sound_dug.deSerialize(is, version);
635
636         // read legacy properties
637         legacy_facedir_simple = readU8(is);
638         legacy_wallmounted = readU8(is);
639
640         try {
641                 node_dig_prediction = deSerializeString16(is);
642
643                 u8 tmp = readU8(is);
644                 if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
645                         throw SerializationError("");
646                 leveled_max = tmp;
647
648                 tmp = readU8(is);
649                 if (is.eof())
650                         throw SerializationError("");
651                 alpha = static_cast<enum AlphaMode>(tmp);
652
653                 tmp = readU8(is);
654                 if (is.eof())
655                         throw SerializationError("");
656                 move_resistance = tmp;
657
658                 tmp = readU8(is);
659                 if (is.eof())
660                         throw SerializationError("");
661                 liquid_move_physics = tmp;
662         } catch(SerializationError &e) {};
663 }
664
665 #ifndef SERVER
666 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
667                 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
668                 u8 material_type, u32 shader_id, bool backface_culling,
669                 const TextureSettings &tsettings)
670 {
671         layer->shader_id     = shader_id;
672         layer->texture       = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
673         layer->material_type = material_type;
674
675         bool has_scale = tiledef.scale > 0;
676         bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
677                 (tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
678         if (use_autoscale && layer->texture) {
679                 auto texture_size = layer->texture->getOriginalSize();
680                 float base_size = tsettings.node_texture_size;
681                 float size = std::fmin(texture_size.Width, texture_size.Height);
682                 layer->scale = std::fmax(base_size, size) / base_size;
683         } else if (has_scale) {
684                 layer->scale = tiledef.scale;
685         } else {
686                 layer->scale = 1;
687         }
688         if (!tile.world_aligned)
689                 layer->scale = 1;
690
691         layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
692
693         // Material flags
694         layer->material_flags = 0;
695         if (backface_culling)
696                 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
697         if (tiledef.animation.type != TAT_NONE)
698                 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
699         if (tiledef.tileable_horizontal)
700                 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
701         if (tiledef.tileable_vertical)
702                 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
703
704         // Color
705         layer->has_color = tiledef.has_color;
706         if (tiledef.has_color)
707                 layer->color = tiledef.color;
708         else
709                 layer->color = color;
710
711         // Animation parameters
712         int frame_count = 1;
713         if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
714                 assert(layer->texture);
715                 int frame_length_ms;
716                 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
717                                 &frame_count, &frame_length_ms, NULL);
718                 layer->animation_frame_count = frame_count;
719                 layer->animation_frame_length_ms = frame_length_ms;
720         }
721
722         if (frame_count == 1) {
723                 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
724         } else {
725                 assert(layer->texture);
726                 if (!layer->frames)
727                         layer->frames = new std::vector<FrameSpec>();
728                 layer->frames->resize(frame_count);
729
730                 std::ostringstream os(std::ios::binary);
731                 for (int i = 0; i < frame_count; i++) {
732                         FrameSpec frame;
733
734                         os.str("");
735                         os << tiledef.name;
736                         tiledef.animation.getTextureModifer(os,
737                                         layer->texture->getOriginalSize(), i);
738
739                         frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
740                         if (layer->normal_texture)
741                                 frame.normal_texture = tsrc->getNormalTexture(os.str());
742                         frame.flags_texture = layer->flags_texture;
743                         (*layer->frames)[i] = frame;
744                 }
745         }
746 }
747
748 bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length)
749 {
750         video::IVideoDriver *driver = RenderingEngine::get_video_driver();
751         static thread_local bool long_warning_printed = false;
752         std::set<std::string> seen;
753
754         for (int i = 0; i < length; i++) {
755                 if (seen.find(tiles[i].name) != seen.end())
756                         continue;
757                 seen.insert(tiles[i].name);
758
759                 // Load the texture and see if there's any transparent pixels
760                 video::ITexture *texture = tsrc->getTexture(tiles[i].name);
761                 video::IImage *image = driver->createImage(texture,
762                         core::position2d<s32>(0, 0), texture->getOriginalSize());
763                 if (!image)
764                         continue;
765                 core::dimension2d<u32> dim = image->getDimension();
766                 bool ok = true;
767                 for (u16 x = 0; x < dim.Width; x++) {
768                         for (u16 y = 0; y < dim.Height; y++) {
769                                 if (image->getPixel(x, y).getAlpha() < 255) {
770                                         ok = false;
771                                         goto break_loop;
772                                 }
773                         }
774                 }
775
776 break_loop:
777                 image->drop();
778                 if (ok)
779                         continue;
780                 warningstream << "Texture \"" << tiles[i].name << "\" of "
781                         << name << " has transparency, assuming "
782                         "use_texture_alpha = \"clip\"." << std::endl;
783                 if (!long_warning_printed) {
784                         warningstream << "  This warning can be a false-positive if "
785                                 "unused pixels in the texture are transparent. However if "
786                                 "it is meant to be transparent, you *MUST* update the "
787                                 "nodedef and set use_texture_alpha = \"clip\"! This "
788                                 "compatibility code will be removed in a few releases."
789                                 << std::endl;
790                         long_warning_printed = true;
791                 }
792                 return true;
793         }
794         return false;
795 }
796
797 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
798 {
799         if (style == ALIGN_STYLE_WORLD)
800                 return true;
801         if (mode == WORLDALIGN_DISABLE)
802                 return false;
803         if (style == ALIGN_STYLE_USER_DEFINED)
804                 return true;
805         if (drawtype == NDT_NORMAL)
806                 return mode >= WORLDALIGN_FORCE;
807         if (drawtype == NDT_NODEBOX)
808                 return mode >= WORLDALIGN_FORCE_NODEBOX;
809         return false;
810 }
811
812 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
813         scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
814 {
815         // minimap pixel color - the average color of a texture
816         if (tsettings.enable_minimap && !tiledef[0].name.empty())
817                 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
818
819         // Figure out the actual tiles to use
820         TileDef tdef[6];
821         for (u32 j = 0; j < 6; j++) {
822                 tdef[j] = tiledef[j];
823                 if (tdef[j].name.empty()) {
824                         tdef[j].name = "no_texture.png";
825                         tdef[j].backface_culling = false;
826                 }
827         }
828         // also the overlay tiles
829         TileDef tdef_overlay[6];
830         for (u32 j = 0; j < 6; j++)
831                 tdef_overlay[j] = tiledef_overlay[j];
832         // also the special tiles
833         TileDef tdef_spec[6];
834         for (u32 j = 0; j < CF_SPECIAL_COUNT; j++) {
835                 tdef_spec[j] = tiledef_special[j];
836         }
837
838         bool is_liquid = false;
839
840         if (alpha == ALPHAMODE_LEGACY_COMPAT) {
841                 // Before working with the alpha mode, resolve any legacy kludges
842                 alpha = textureAlphaCheck(tsrc, tdef, 6) ? ALPHAMODE_CLIP : ALPHAMODE_OPAQUE;
843         }
844
845         MaterialType material_type = alpha == ALPHAMODE_OPAQUE ?
846                 TILE_MATERIAL_OPAQUE : (alpha == ALPHAMODE_CLIP ? TILE_MATERIAL_BASIC :
847                 TILE_MATERIAL_ALPHA);
848
849         switch (drawtype) {
850         default:
851         case NDT_NORMAL:
852                 solidness = 2;
853                 break;
854         case NDT_AIRLIKE:
855                 solidness = 0;
856                 break;
857         case NDT_LIQUID:
858                 if (tsettings.opaque_water)
859                         alpha = ALPHAMODE_OPAQUE;
860                 solidness = 1;
861                 is_liquid = true;
862                 break;
863         case NDT_FLOWINGLIQUID:
864                 solidness = 0;
865                 if (tsettings.opaque_water)
866                         alpha = ALPHAMODE_OPAQUE;
867                 is_liquid = true;
868                 break;
869         case NDT_GLASSLIKE:
870                 solidness = 0;
871                 visual_solidness = 1;
872                 break;
873         case NDT_GLASSLIKE_FRAMED:
874                 solidness = 0;
875                 visual_solidness = 1;
876                 break;
877         case NDT_GLASSLIKE_FRAMED_OPTIONAL:
878                 solidness = 0;
879                 visual_solidness = 1;
880                 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
881                 break;
882         case NDT_ALLFACES:
883                 solidness = 0;
884                 visual_solidness = 1;
885                 break;
886         case NDT_ALLFACES_OPTIONAL:
887                 if (tsettings.leaves_style == LEAVES_FANCY) {
888                         drawtype = NDT_ALLFACES;
889                         solidness = 0;
890                         visual_solidness = 1;
891                 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
892                         for (u32 j = 0; j < 6; j++) {
893                                 if (!tdef_spec[j].name.empty())
894                                         tdef[j].name = tdef_spec[j].name;
895                         }
896                         drawtype = NDT_GLASSLIKE;
897                         solidness = 0;
898                         visual_solidness = 1;
899                 } else {
900                         drawtype = NDT_NORMAL;
901                         solidness = 2;
902                         for (TileDef &td : tdef)
903                                 td.name += std::string("^[noalpha");
904                 }
905                 if (waving >= 1)
906                         material_type = TILE_MATERIAL_WAVING_LEAVES;
907                 break;
908         case NDT_PLANTLIKE:
909                 solidness = 0;
910                 if (waving >= 1)
911                         material_type = TILE_MATERIAL_WAVING_PLANTS;
912                 break;
913         case NDT_FIRELIKE:
914                 solidness = 0;
915                 break;
916         case NDT_MESH:
917         case NDT_NODEBOX:
918                 solidness = 0;
919                 if (waving == 1) {
920                         material_type = TILE_MATERIAL_WAVING_PLANTS;
921                 } else if (waving == 2) {
922                         material_type = TILE_MATERIAL_WAVING_LEAVES;
923                 } else if (waving == 3) {
924                         material_type = alpha == ALPHAMODE_OPAQUE ?
925                                 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
926                                 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
927                 }
928                 break;
929         case NDT_TORCHLIKE:
930         case NDT_SIGNLIKE:
931         case NDT_FENCELIKE:
932         case NDT_RAILLIKE:
933                 solidness = 0;
934                 break;
935         case NDT_PLANTLIKE_ROOTED:
936                 solidness = 2;
937                 break;
938         }
939
940         if (is_liquid) {
941                 if (waving == 3) {
942                         material_type = alpha == ALPHAMODE_OPAQUE ?
943                                 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
944                                 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
945                 } else {
946                         material_type = alpha == ALPHAMODE_OPAQUE ? TILE_MATERIAL_LIQUID_OPAQUE :
947                                 TILE_MATERIAL_LIQUID_TRANSPARENT;
948                 }
949         }
950
951         u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
952
953         MaterialType overlay_material = material_type;
954         if (overlay_material == TILE_MATERIAL_OPAQUE)
955                 overlay_material = TILE_MATERIAL_BASIC;
956         else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
957                 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
958
959         u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
960
961         // Tiles (fill in f->tiles[])
962         for (u16 j = 0; j < 6; j++) {
963                 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
964                                 tsettings.world_aligned_mode, drawtype);
965                 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
966                                 color, material_type, tile_shader,
967                                 tdef[j].backface_culling, tsettings);
968                 if (!tdef_overlay[j].name.empty())
969                         fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
970                                         color, overlay_material, overlay_shader,
971                                         tdef[j].backface_culling, tsettings);
972         }
973
974         MaterialType special_material = material_type;
975         if (drawtype == NDT_PLANTLIKE_ROOTED) {
976                 if (waving == 1)
977                         special_material = TILE_MATERIAL_WAVING_PLANTS;
978                 else if (waving == 2)
979                         special_material = TILE_MATERIAL_WAVING_LEAVES;
980         }
981         u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
982
983         // Special tiles (fill in f->special_tiles[])
984         for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
985                 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
986                                 color, special_material, special_shader,
987                                 tdef_spec[j].backface_culling, tsettings);
988
989         if (param_type_2 == CPT2_COLOR ||
990                         param_type_2 == CPT2_COLORED_FACEDIR ||
991                         param_type_2 == CPT2_COLORED_WALLMOUNTED ||
992                         param_type_2 == CPT2_COLORED_DEGROTATE)
993                 palette = tsrc->getPalette(palette_name);
994
995         if (drawtype == NDT_MESH && !mesh.empty()) {
996                 // Meshnode drawtype
997                 // Read the mesh and apply scale
998                 mesh_ptr[0] = client->getMesh(mesh);
999                 if (mesh_ptr[0]){
1000                         v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
1001                         scaleMesh(mesh_ptr[0], scale);
1002                         recalculateBoundingBox(mesh_ptr[0]);
1003                         meshmanip->recalculateNormals(mesh_ptr[0], true, false);
1004                 }
1005         }
1006
1007         //Cache 6dfacedir and wallmounted rotated clones of meshes
1008         if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
1009                         (param_type_2 == CPT2_FACEDIR
1010                         || param_type_2 == CPT2_COLORED_FACEDIR)) {
1011                 for (u16 j = 1; j < 24; j++) {
1012                         mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
1013                         rotateMeshBy6dFacedir(mesh_ptr[j], j);
1014                         recalculateBoundingBox(mesh_ptr[j]);
1015                         meshmanip->recalculateNormals(mesh_ptr[j], true, false);
1016                 }
1017         } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
1018                         && (param_type_2 == CPT2_WALLMOUNTED ||
1019                         param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
1020                 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
1021                 for (u16 j = 1; j < 6; j++) {
1022                         mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
1023                         rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
1024                         recalculateBoundingBox(mesh_ptr[j]);
1025                         meshmanip->recalculateNormals(mesh_ptr[j], true, false);
1026                 }
1027                 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
1028                 recalculateBoundingBox(mesh_ptr[0]);
1029                 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
1030         }
1031 }
1032 #endif
1033
1034 /*
1035         NodeDefManager
1036 */
1037
1038
1039
1040
1041 NodeDefManager::NodeDefManager()
1042 {
1043         clear();
1044 }
1045
1046
1047 NodeDefManager::~NodeDefManager()
1048 {
1049 #ifndef SERVER
1050         for (ContentFeatures &f : m_content_features) {
1051                 for (auto &j : f.mesh_ptr) {
1052                         if (j)
1053                                 j->drop();
1054                 }
1055         }
1056 #endif
1057 }
1058
1059
1060 void NodeDefManager::clear()
1061 {
1062         m_content_features.clear();
1063         m_name_id_mapping.clear();
1064         m_name_id_mapping_with_aliases.clear();
1065         m_group_to_items.clear();
1066         m_next_id = 0;
1067         m_selection_box_union.reset(0,0,0);
1068         m_selection_box_int_union.reset(0,0,0);
1069
1070         resetNodeResolveState();
1071
1072         u32 initial_length = 0;
1073         initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1074         initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1075         initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1076         m_content_features.resize(initial_length);
1077
1078         // Set CONTENT_UNKNOWN
1079         {
1080                 ContentFeatures f;
1081                 f.name = "unknown";
1082                 TileDef unknownTile;
1083                 unknownTile.name = "unknown_node.png";
1084                 for (int t = 0; t < 6; t++)
1085                         f.tiledef[t] = unknownTile;
1086                 // Insert directly into containers
1087                 content_t c = CONTENT_UNKNOWN;
1088                 m_content_features[c] = f;
1089                 addNameIdMapping(c, f.name);
1090         }
1091
1092         // Set CONTENT_AIR
1093         {
1094                 ContentFeatures f;
1095                 f.name                = "air";
1096                 f.drawtype            = NDT_AIRLIKE;
1097                 f.param_type          = CPT_LIGHT;
1098                 f.light_propagates    = true;
1099                 f.sunlight_propagates = true;
1100                 f.walkable            = false;
1101                 f.pointable           = false;
1102                 f.diggable            = false;
1103                 f.buildable_to        = true;
1104                 f.floodable           = true;
1105                 f.is_ground_content   = true;
1106                 // Insert directly into containers
1107                 content_t c = CONTENT_AIR;
1108                 m_content_features[c] = f;
1109                 addNameIdMapping(c, f.name);
1110         }
1111
1112         // Set CONTENT_IGNORE
1113         {
1114                 ContentFeatures f;
1115                 f.name                = "ignore";
1116                 f.drawtype            = NDT_AIRLIKE;
1117                 f.param_type          = CPT_NONE;
1118                 f.light_propagates    = false;
1119                 f.sunlight_propagates = false;
1120                 f.walkable            = false;
1121                 f.pointable           = false;
1122                 f.diggable            = false;
1123                 f.buildable_to        = true; // A way to remove accidental CONTENT_IGNOREs
1124                 f.is_ground_content   = true;
1125                 // Insert directly into containers
1126                 content_t c = CONTENT_IGNORE;
1127                 m_content_features[c] = f;
1128                 addNameIdMapping(c, f.name);
1129         }
1130 }
1131
1132
1133 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1134 {
1135         std::unordered_map<std::string, content_t>::const_iterator
1136                 i = m_name_id_mapping_with_aliases.find(name);
1137         if(i == m_name_id_mapping_with_aliases.end())
1138                 return false;
1139         result = i->second;
1140         return true;
1141 }
1142
1143
1144 content_t NodeDefManager::getId(const std::string &name) const
1145 {
1146         content_t id = CONTENT_IGNORE;
1147         getId(name, id);
1148         return id;
1149 }
1150
1151
1152 bool NodeDefManager::getIds(const std::string &name,
1153                 std::vector<content_t> &result) const
1154 {
1155         //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1156         if (name.substr(0,6) != "group:") {
1157                 content_t id = CONTENT_IGNORE;
1158                 bool exists = getId(name, id);
1159                 if (exists)
1160                         result.push_back(id);
1161                 return exists;
1162         }
1163         std::string group = name.substr(6);
1164
1165         std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1166                 i = m_group_to_items.find(group);
1167         if (i == m_group_to_items.end())
1168                 return true;
1169
1170         const std::vector<content_t> &items = i->second;
1171         result.insert(result.end(), items.begin(), items.end());
1172         //printf("getIds: %dus\n", t.stop());
1173         return true;
1174 }
1175
1176
1177 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1178 {
1179         content_t id = CONTENT_UNKNOWN;
1180         getId(name, id);
1181         return get(id);
1182 }
1183
1184
1185 // returns CONTENT_IGNORE if no free ID found
1186 content_t NodeDefManager::allocateId()
1187 {
1188         for (content_t id = m_next_id;
1189                         id >= m_next_id; // overflow?
1190                         ++id) {
1191                 while (id >= m_content_features.size()) {
1192                         m_content_features.emplace_back();
1193                 }
1194                 const ContentFeatures &f = m_content_features[id];
1195                 if (f.name.empty()) {
1196                         m_next_id = id + 1;
1197                         return id;
1198                 }
1199         }
1200         // If we arrive here, an overflow occurred in id.
1201         // That means no ID was found
1202         return CONTENT_IGNORE;
1203 }
1204
1205
1206 /*!
1207  * Returns the smallest box that contains all boxes
1208  * in the vector. Box_union is expanded.
1209  * @param[in]      boxes     the vector containing the boxes
1210  * @param[in, out] box_union the union of the arguments
1211  */
1212 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1213 {
1214         for (const aabb3f &box : boxes) {
1215                 box_union->addInternalBox(box);
1216         }
1217 }
1218
1219
1220 /*!
1221  * Returns a box that contains the nodebox in every case.
1222  * The argument node_union is expanded.
1223  * @param[in]      nodebox  the nodebox to be measured
1224  * @param[in]      features  used to decide whether the nodebox
1225  * can be rotated
1226  * @param[in, out] box_union the union of the arguments
1227  */
1228 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1229         aabb3f *box_union)
1230 {
1231         switch(nodebox.type) {
1232                 case NODEBOX_FIXED:
1233                 case NODEBOX_LEVELED: {
1234                         // Raw union
1235                         aabb3f half_processed(0, 0, 0, 0, 0, 0);
1236                         boxVectorUnion(nodebox.fixed, &half_processed);
1237                         // Set leveled boxes to maximal
1238                         if (nodebox.type == NODEBOX_LEVELED) {
1239                                 half_processed.MaxEdge.Y = +BS / 2;
1240                         }
1241                         if (features.param_type_2 == CPT2_FACEDIR ||
1242                                         features.param_type_2 == CPT2_COLORED_FACEDIR) {
1243                                 // Get maximal coordinate
1244                                 f32 coords[] = {
1245                                         fabsf(half_processed.MinEdge.X),
1246                                         fabsf(half_processed.MinEdge.Y),
1247                                         fabsf(half_processed.MinEdge.Z),
1248                                         fabsf(half_processed.MaxEdge.X),
1249                                         fabsf(half_processed.MaxEdge.Y),
1250                                         fabsf(half_processed.MaxEdge.Z) };
1251                                 f32 max = 0;
1252                                 for (float coord : coords) {
1253                                         if (max < coord) {
1254                                                 max = coord;
1255                                         }
1256                                 }
1257                                 // Add the union of all possible rotated boxes
1258                                 box_union->addInternalPoint(-max, -max, -max);
1259                                 box_union->addInternalPoint(+max, +max, +max);
1260                         } else {
1261                                 box_union->addInternalBox(half_processed);
1262                         }
1263                         break;
1264                 }
1265                 case NODEBOX_WALLMOUNTED: {
1266                         // Add fix boxes
1267                         box_union->addInternalBox(nodebox.wall_top);
1268                         box_union->addInternalBox(nodebox.wall_bottom);
1269                         // Find maximal coordinate in the X-Z plane
1270                         f32 coords[] = {
1271                                 fabsf(nodebox.wall_side.MinEdge.X),
1272                                 fabsf(nodebox.wall_side.MinEdge.Z),
1273                                 fabsf(nodebox.wall_side.MaxEdge.X),
1274                                 fabsf(nodebox.wall_side.MaxEdge.Z) };
1275                         f32 max = 0;
1276                         for (float coord : coords) {
1277                                 if (max < coord) {
1278                                         max = coord;
1279                                 }
1280                         }
1281                         // Add the union of all possible rotated boxes
1282                         box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1283                         box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1284                         break;
1285                 }
1286                 case NODEBOX_CONNECTED: {
1287                         // Add all possible connected boxes
1288                         boxVectorUnion(nodebox.fixed,               box_union);
1289                         boxVectorUnion(nodebox.connect_top,         box_union);
1290                         boxVectorUnion(nodebox.connect_bottom,      box_union);
1291                         boxVectorUnion(nodebox.connect_front,       box_union);
1292                         boxVectorUnion(nodebox.connect_left,        box_union);
1293                         boxVectorUnion(nodebox.connect_back,        box_union);
1294                         boxVectorUnion(nodebox.connect_right,       box_union);
1295                         boxVectorUnion(nodebox.disconnected_top,    box_union);
1296                         boxVectorUnion(nodebox.disconnected_bottom, box_union);
1297                         boxVectorUnion(nodebox.disconnected_front,  box_union);
1298                         boxVectorUnion(nodebox.disconnected_left,   box_union);
1299                         boxVectorUnion(nodebox.disconnected_back,   box_union);
1300                         boxVectorUnion(nodebox.disconnected_right,  box_union);
1301                         boxVectorUnion(nodebox.disconnected,        box_union);
1302                         boxVectorUnion(nodebox.disconnected_sides,  box_union);
1303                         break;
1304                 }
1305                 default: {
1306                         // NODEBOX_REGULAR
1307                         box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1308                         box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1309                 }
1310         }
1311 }
1312
1313
1314 inline void NodeDefManager::fixSelectionBoxIntUnion()
1315 {
1316         m_selection_box_int_union.MinEdge.X = floorf(
1317                 m_selection_box_union.MinEdge.X / BS + 0.5f);
1318         m_selection_box_int_union.MinEdge.Y = floorf(
1319                 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1320         m_selection_box_int_union.MinEdge.Z = floorf(
1321                 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1322         m_selection_box_int_union.MaxEdge.X = ceilf(
1323                 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1324         m_selection_box_int_union.MaxEdge.Y = ceilf(
1325                 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1326         m_selection_box_int_union.MaxEdge.Z = ceilf(
1327                 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1328 }
1329
1330
1331 void NodeDefManager::eraseIdFromGroups(content_t id)
1332 {
1333         // For all groups in m_group_to_items...
1334         for (auto iter_groups = m_group_to_items.begin();
1335                         iter_groups != m_group_to_items.end();) {
1336                 // Get the group items vector.
1337                 std::vector<content_t> &items = iter_groups->second;
1338
1339                 // Remove any occurence of the id in the group items vector.
1340                 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1341
1342                 // If group is empty, erase its vector from the map.
1343                 if (items.empty())
1344                         iter_groups = m_group_to_items.erase(iter_groups);
1345                 else
1346                         ++iter_groups;
1347         }
1348 }
1349
1350
1351 // IWritableNodeDefManager
1352 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1353 {
1354         // Pre-conditions
1355         assert(name != "");
1356         assert(name != "ignore");
1357         assert(name == def.name);
1358
1359         content_t id = CONTENT_IGNORE;
1360         if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1361                 // Get new id
1362                 id = allocateId();
1363                 if (id == CONTENT_IGNORE) {
1364                         warningstream << "NodeDefManager: Absolute "
1365                                 "limit reached" << std::endl;
1366                         return CONTENT_IGNORE;
1367                 }
1368                 assert(id != CONTENT_IGNORE);
1369                 addNameIdMapping(id, name);
1370         }
1371
1372         // If there is already ContentFeatures registered for this id, clear old groups
1373         if (id < m_content_features.size())
1374                 eraseIdFromGroups(id);
1375
1376         m_content_features[id] = def;
1377         verbosestream << "NodeDefManager: registering content id \"" << id
1378                 << "\": name=\"" << def.name << "\""<<std::endl;
1379
1380         getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1381         fixSelectionBoxIntUnion();
1382
1383         // Add this content to the list of all groups it belongs to
1384         for (const auto &group : def.groups) {
1385                 const std::string &group_name = group.first;
1386                 m_group_to_items[group_name].push_back(id);
1387         }
1388
1389         return id;
1390 }
1391
1392
1393 content_t NodeDefManager::allocateDummy(const std::string &name)
1394 {
1395         assert(name != "");     // Pre-condition
1396         ContentFeatures f;
1397         f.name = name;
1398         return set(name, f);
1399 }
1400
1401
1402 void NodeDefManager::removeNode(const std::string &name)
1403 {
1404         // Pre-condition
1405         assert(name != "");
1406
1407         // Erase name from name ID mapping
1408         content_t id = CONTENT_IGNORE;
1409         if (m_name_id_mapping.getId(name, id)) {
1410                 m_name_id_mapping.eraseName(name);
1411                 m_name_id_mapping_with_aliases.erase(name);
1412         }
1413
1414         eraseIdFromGroups(id);
1415 }
1416
1417
1418 void NodeDefManager::updateAliases(IItemDefManager *idef)
1419 {
1420         std::set<std::string> all;
1421         idef->getAll(all);
1422         m_name_id_mapping_with_aliases.clear();
1423         for (const std::string &name : all) {
1424                 const std::string &convert_to = idef->getAlias(name);
1425                 content_t id;
1426                 if (m_name_id_mapping.getId(convert_to, id)) {
1427                         m_name_id_mapping_with_aliases.insert(
1428                                 std::make_pair(name, id));
1429                 }
1430         }
1431 }
1432
1433 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1434 {
1435         infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1436                 "overrides to textures" << std::endl;
1437
1438         for (const TextureOverride& texture_override : overrides) {
1439                 content_t id;
1440                 if (!getId(texture_override.id, id))
1441                         continue; // Ignore unknown node
1442
1443                 ContentFeatures &nodedef = m_content_features[id];
1444
1445                 // Override tiles
1446                 if (texture_override.hasTarget(OverrideTarget::TOP))
1447                         nodedef.tiledef[0].name = texture_override.texture;
1448
1449                 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1450                         nodedef.tiledef[1].name = texture_override.texture;
1451
1452                 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1453                         nodedef.tiledef[2].name = texture_override.texture;
1454
1455                 if (texture_override.hasTarget(OverrideTarget::LEFT))
1456                         nodedef.tiledef[3].name = texture_override.texture;
1457
1458                 if (texture_override.hasTarget(OverrideTarget::BACK))
1459                         nodedef.tiledef[4].name = texture_override.texture;
1460
1461                 if (texture_override.hasTarget(OverrideTarget::FRONT))
1462                         nodedef.tiledef[5].name = texture_override.texture;
1463
1464
1465                 // Override special tiles, if applicable
1466                 if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1467                         nodedef.tiledef_special[0].name = texture_override.texture;
1468
1469                 if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1470                         nodedef.tiledef_special[1].name = texture_override.texture;
1471
1472                 if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1473                         nodedef.tiledef_special[2].name = texture_override.texture;
1474
1475                 if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1476                         nodedef.tiledef_special[3].name = texture_override.texture;
1477
1478                 if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1479                         nodedef.tiledef_special[4].name = texture_override.texture;
1480
1481                 if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1482                         nodedef.tiledef_special[5].name = texture_override.texture;
1483         }
1484 }
1485
1486 void NodeDefManager::updateTextures(IGameDef *gamedef, void *progress_callback_args)
1487 {
1488 #ifndef SERVER
1489         infostream << "NodeDefManager::updateTextures(): Updating "
1490                 "textures in node definitions" << std::endl;
1491
1492         Client *client = (Client *)gamedef;
1493         ITextureSource *tsrc = client->tsrc();
1494         IShaderSource *shdsrc = client->getShaderSource();
1495         auto smgr = client->getSceneManager();
1496         scene::IMeshManipulator *meshmanip = smgr->getMeshManipulator();
1497         TextureSettings tsettings;
1498         tsettings.readSettings();
1499
1500         u32 size = m_content_features.size();
1501
1502         for (u32 i = 0; i < size; i++) {
1503                 ContentFeatures *f = &(m_content_features[i]);
1504                 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1505                 client->showUpdateProgressTexture(progress_callback_args, i, size);
1506         }
1507 #endif
1508 }
1509
1510 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1511 {
1512         writeU8(os, 1); // version
1513         u16 count = 0;
1514         std::ostringstream os2(std::ios::binary);
1515         for (u32 i = 0; i < m_content_features.size(); i++) {
1516                 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1517                                 || i == CONTENT_UNKNOWN)
1518                         continue;
1519                 const ContentFeatures *f = &m_content_features[i];
1520                 if (f->name.empty())
1521                         continue;
1522                 writeU16(os2, i);
1523                 // Wrap it in a string to allow different lengths without
1524                 // strict version incompatibilities
1525                 std::ostringstream wrapper_os(std::ios::binary);
1526                 f->serialize(wrapper_os, protocol_version);
1527                 os2<<serializeString16(wrapper_os.str());
1528
1529                 // must not overflow
1530                 u16 next = count + 1;
1531                 FATAL_ERROR_IF(next < count, "Overflow");
1532                 count++;
1533         }
1534         writeU16(os, count);
1535         os << serializeString32(os2.str());
1536 }
1537
1538
1539 void NodeDefManager::deSerialize(std::istream &is)
1540 {
1541         clear();
1542         int version = readU8(is);
1543         if (version != 1)
1544                 throw SerializationError("unsupported NodeDefinitionManager version");
1545         u16 count = readU16(is);
1546         std::istringstream is2(deSerializeString32(is), std::ios::binary);
1547         ContentFeatures f;
1548         for (u16 n = 0; n < count; n++) {
1549                 u16 i = readU16(is2);
1550
1551                 // Read it from the string wrapper
1552                 std::string wrapper = deSerializeString16(is2);
1553                 std::istringstream wrapper_is(wrapper, std::ios::binary);
1554                 f.deSerialize(wrapper_is);
1555
1556                 // Check error conditions
1557                 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1558                         warningstream << "NodeDefManager::deSerialize(): "
1559                                 "not changing builtin node " << i << std::endl;
1560                         continue;
1561                 }
1562                 if (f.name.empty()) {
1563                         warningstream << "NodeDefManager::deSerialize(): "
1564                                 "received empty name" << std::endl;
1565                         continue;
1566                 }
1567
1568                 // Ignore aliases
1569                 u16 existing_id;
1570                 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1571                         warningstream << "NodeDefManager::deSerialize(): "
1572                                 "already defined with different ID: " << f.name << std::endl;
1573                         continue;
1574                 }
1575
1576                 // All is ok, add node definition with the requested ID
1577                 if (i >= m_content_features.size())
1578                         m_content_features.resize((u32)(i) + 1);
1579                 m_content_features[i] = f;
1580                 addNameIdMapping(i, f.name);
1581                 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1582
1583                 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1584                 fixSelectionBoxIntUnion();
1585         }
1586
1587         // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1588         // are not sent, resolve them client-side too.
1589         resolveCrossrefs();
1590 }
1591
1592
1593 void NodeDefManager::addNameIdMapping(content_t i, const std::string &name)
1594 {
1595         m_name_id_mapping.set(i, name);
1596         m_name_id_mapping_with_aliases.emplace(name, i);
1597 }
1598
1599
1600 NodeDefManager *createNodeDefManager()
1601 {
1602         return new NodeDefManager();
1603 }
1604
1605
1606 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1607 {
1608         nr->m_ndef = this;
1609         if (m_node_registration_complete)
1610                 nr->nodeResolveInternal();
1611         else
1612                 m_pending_resolve_callbacks.push_back(nr);
1613 }
1614
1615
1616 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1617 {
1618         size_t len = m_pending_resolve_callbacks.size();
1619         for (size_t i = 0; i != len; i++) {
1620                 if (nr != m_pending_resolve_callbacks[i])
1621                         continue;
1622
1623                 len--;
1624                 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1625                 m_pending_resolve_callbacks.resize(len);
1626                 return true;
1627         }
1628
1629         return false;
1630 }
1631
1632
1633 void NodeDefManager::runNodeResolveCallbacks()
1634 {
1635         for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1636                 NodeResolver *nr = m_pending_resolve_callbacks[i];
1637                 nr->nodeResolveInternal();
1638         }
1639
1640         m_pending_resolve_callbacks.clear();
1641 }
1642
1643
1644 void NodeDefManager::resetNodeResolveState()
1645 {
1646         m_node_registration_complete = false;
1647         m_pending_resolve_callbacks.clear();
1648 }
1649
1650 static void removeDupes(std::vector<content_t> &list)
1651 {
1652         std::sort(list.begin(), list.end());
1653         auto new_end = std::unique(list.begin(), list.end());
1654         list.erase(new_end, list.end());
1655 }
1656
1657 void NodeDefManager::resolveCrossrefs()
1658 {
1659         for (ContentFeatures &f : m_content_features) {
1660                 if (f.liquid_type != LIQUID_NONE || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
1661                         f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1662                         f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1663                         continue;
1664                 }
1665                 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1666                         continue;
1667
1668                 for (const std::string &name : f.connects_to) {
1669                         getIds(name, f.connects_to_ids);
1670                 }
1671                 removeDupes(f.connects_to_ids);
1672         }
1673 }
1674
1675 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1676         u8 connect_face) const
1677 {
1678         const ContentFeatures &f1 = get(from);
1679
1680         if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1681                 return false;
1682
1683         // lookup target in connected set
1684         if (!CONTAINS(f1.connects_to_ids, to.param0))
1685                 return false;
1686
1687         const ContentFeatures &f2 = get(to);
1688
1689         if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1690                 // ignores actually looking if back connection exists
1691                 return CONTAINS(f2.connects_to_ids, from.param0);
1692
1693         // does to node declare usable faces?
1694         if (f2.connect_sides > 0) {
1695                 if ((f2.param_type_2 == CPT2_FACEDIR ||
1696                                 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1697                                 && (connect_face >= 4)) {
1698                         static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1699                                 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1700                                 0, // 4 - back
1701                                 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1702                                 0, // 8 - right
1703                                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1704                                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1705                                 0, // 16 - front
1706                                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1707                                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1708                                 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1709                                 };
1710                         return (f2.connect_sides
1711                                 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1712                 }
1713                 return (f2.connect_sides & connect_face);
1714         }
1715         // the target is just a regular node, so connect no matter back connection
1716         return true;
1717 }
1718
1719 ////
1720 //// NodeResolver
1721 ////
1722
1723 NodeResolver::NodeResolver()
1724 {
1725         reset();
1726 }
1727
1728
1729 NodeResolver::~NodeResolver()
1730 {
1731         if (!m_resolve_done && m_ndef)
1732                 m_ndef->cancelNodeResolveCallback(this);
1733 }
1734
1735
1736 void NodeResolver::cloneTo(NodeResolver *res) const
1737 {
1738         FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1739                 " after resolving has completed");
1740         /* We don't actually do anything significant. Since the node resolving has
1741          * already completed, the class that called us will already have the
1742          * resolved IDs in its data structures (which it copies on its own) */
1743         res->m_ndef = m_ndef;
1744         res->m_resolve_done = true;
1745 }
1746
1747
1748 void NodeResolver::nodeResolveInternal()
1749 {
1750         m_nodenames_idx   = 0;
1751         m_nnlistsizes_idx = 0;
1752
1753         resolveNodeNames();
1754         m_resolve_done = true;
1755
1756         m_nodenames.clear();
1757         m_nnlistsizes.clear();
1758 }
1759
1760
1761 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1762         const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1763 {
1764         if (m_nodenames_idx == m_nodenames.size()) {
1765                 *result_out = c_fallback;
1766                 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1767                 return false;
1768         }
1769
1770         content_t c;
1771         std::string name = m_nodenames[m_nodenames_idx++];
1772
1773         bool success = m_ndef->getId(name, c);
1774         if (!success && !node_alt.empty()) {
1775                 name = node_alt;
1776                 success = m_ndef->getId(name, c);
1777         }
1778
1779         if (!success) {
1780                 if (error_on_fallback)
1781                         errorstream << "NodeResolver: failed to resolve node name '" << name
1782                                 << "'." << std::endl;
1783                 c = c_fallback;
1784         }
1785
1786         *result_out = c;
1787         return success;
1788 }
1789
1790
1791 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1792         bool all_required, content_t c_fallback)
1793 {
1794         bool success = true;
1795
1796         if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1797                 errorstream << "NodeResolver: no more node lists" << std::endl;
1798                 return false;
1799         }
1800
1801         size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1802
1803         while (length--) {
1804                 if (m_nodenames_idx == m_nodenames.size()) {
1805                         errorstream << "NodeResolver: no more nodes in list" << std::endl;
1806                         return false;
1807                 }
1808
1809                 content_t c;
1810                 std::string &name = m_nodenames[m_nodenames_idx++];
1811
1812                 if (name.substr(0,6) != "group:") {
1813                         if (m_ndef->getId(name, c)) {
1814                                 result_out->push_back(c);
1815                         } else if (all_required) {
1816                                 errorstream << "NodeResolver: failed to resolve node name '"
1817                                         << name << "'." << std::endl;
1818                                 result_out->push_back(c_fallback);
1819                                 success = false;
1820                         }
1821                 } else {
1822                         m_ndef->getIds(name, *result_out);
1823                 }
1824         }
1825
1826         return success;
1827 }
1828
1829 void NodeResolver::reset(bool resolve_done)
1830 {
1831         m_nodenames.clear();
1832         m_nodenames_idx = 0;
1833         m_nnlistsizes.clear();
1834         m_nnlistsizes_idx = 0;
1835
1836         m_resolve_done = resolve_done;
1837
1838         m_nodenames.reserve(16);
1839         m_nnlistsizes.reserve(4);
1840 }