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