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