3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
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>
33 #include "nameidmapping.h"
34 #include "util/numeric.h"
35 #include "util/serialize.h"
36 #include "exceptions.h"
40 #include <fstream> // Used in applyTextureOverrides()
50 type = NODEBOX_REGULAR;
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
59 connect_bottom.clear();
60 connect_front.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();
71 disconnected_sides.clear();
74 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
85 writeU16(os, fixed.size());
86 for (const aabb3f &nodebox : fixed) {
87 writeV3F32(os, nodebox.MinEdge);
88 writeV3F32(os, nodebox.MaxEdge);
91 case NODEBOX_WALLMOUNTED:
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);
101 case NODEBOX_CONNECTED:
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); \
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);
133 void NodeBox::deSerialize(std::istream &is)
135 int version = readU8(is);
137 throw SerializationError("unsupported NodeBox version");
141 type = (enum NodeBoxType)readU8(is);
143 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
145 u16 fixed_count = readU16(is);
149 box.MinEdge = readV3F32(is);
150 box.MaxEdge = readV3F32(is);
151 fixed.push_back(box);
154 else if(type == NODEBOX_WALLMOUNTED)
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);
163 else if (type == NODEBOX_CONNECTED)
165 #define READBOXES(box) { \
166 count = readU16(is); \
167 (box).reserve(count); \
169 v3f min = readV3F32(is); \
170 v3f max = readV3F32(is); \
171 (box).emplace_back(min, max); }; }
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);
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)
204 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
206 // protocol_version >= 36
208 writeU8(os, version);
210 os << serializeString16(name);
211 animation.serialize(os, version);
212 bool has_scale = scale > 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;
221 flags |= TILE_FLAG_HAS_COLOR;
223 flags |= TILE_FLAG_HAS_SCALE;
224 if (align_style != ALIGN_STYLE_NODE)
225 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
228 writeU8(os, color.getRed());
229 writeU8(os, color.getGreen());
230 writeU8(os, color.getBlue());
234 if (align_style != ALIGN_STYLE_NODE)
235 writeU8(os, align_style);
238 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
239 NodeDrawType drawtype)
241 int version = readU8(is);
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;
254 color.setRed(readU8(is));
255 color.setGreen(readU8(is));
256 color.setBlue(readU8(is));
258 scale = has_scale ? readU8(is) : 0;
260 align_style = static_cast<AlignStyle>(readU8(is));
262 align_style = ALIGN_STYLE_NODE;
265 void TextureSettings::readSettings()
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");
277 // Mesh cache is not supported in combination with smooth lighting
279 enable_mesh_cache = false;
281 if (leaves_style_str == "fancy") {
282 leaves_style = LEAVES_FANCY;
283 } else if (leaves_style_str == "simple") {
284 leaves_style = LEAVES_SIMPLE;
286 leaves_style = LEAVES_OPAQUE;
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;
296 world_aligned_mode = WORLDALIGN_DISABLE;
298 if (autoscale_mode_str == "enable")
299 autoscale_mode = AUTOSCALE_ENABLE;
300 else if (autoscale_mode_str == "force")
301 autoscale_mode = AUTOSCALE_FORCE;
303 autoscale_mode = AUTOSCALE_DISABLE;
310 ContentFeatures::ContentFeatures()
315 ContentFeatures::~ContentFeatures()
318 for (u16 j = 0; j < 6; j++) {
319 delete tiles[j].layers[0].frames;
320 delete tiles[j].layers[1].frames;
322 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
323 delete special_tiles[j].layers[0].frames;
327 void ContentFeatures::reset()
334 visual_solidness = 0;
335 backface_culling = true;
338 has_on_construct = false;
339 has_on_destruct = false;
340 has_after_destruct = false;
344 NOTE: Most of this is always overridden by the default values given
349 // Unknown nodes can be dug
350 groups["dig_immediate"] = 2;
351 drawtype = NDT_NORMAL;
354 for (auto &i : mesh_ptr)
356 minimap_color = video::SColor(0, 0, 0, 0);
359 for (auto &i : tiledef)
361 for (auto &j : tiledef_special)
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;
374 buildable_to = false;
376 rightclickable = true;
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;
389 damage_per_second = 0;
390 node_box = NodeBox();
391 selection_box = NodeBox();
392 collision_box = NodeBox();
394 legacy_facedir_simple = false;
395 legacy_wallmounted = false;
396 sound_footstep = SimpleSoundSpec();
397 sound_dig = SimpleSoundSpec("__group");
398 sound_dug = SimpleSoundSpec();
400 connects_to_ids.clear();
402 color = video::SColor(0xFFFFFFFF);
405 node_dig_prediction = "air";
408 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
410 const u8 version = CONTENTFEATURES_VERSION;
411 writeU8(os, version);
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);
420 writeU8(os, param_type);
421 writeU8(os, param_type_2);
424 writeU8(os, drawtype);
425 os << serializeString16(mesh);
426 writeF32(os, visual_scale);
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);
437 writeU8(os, color.getRed());
438 writeU8(os, color.getGreen());
439 writeU8(os, color.getBlue());
440 os << serializeString16(palette_name);
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);
450 writeU8(os, light_propagates);
451 writeU8(os, sunlight_propagates);
452 writeU8(os, light_source);
455 writeU8(os, is_ground_content);
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);
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);
477 node_box.serialize(os, protocol_version);
478 selection_box.serialize(os, protocol_version);
479 collision_box.serialize(os, protocol_version);
482 sound_footstep.serialize(os, version);
483 sound_dig.serialize(os, version);
484 sound_dug.serialize(os, version);
487 writeU8(os, legacy_facedir_simple);
488 writeU8(os, legacy_wallmounted);
490 os << serializeString16(node_dig_prediction);
491 writeU8(os, leveled_max);
494 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
496 // alpha == 0 means that the node is using texture alpha
497 if (alpha == 0 || alpha == 255)
500 for (int i = 0; i < length; i++) {
501 if (tiles[i].name.empty())
504 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
505 tiles[i].name = s.str();
509 void ContentFeatures::deSerialize(std::istream &is)
512 const u8 version = readU8(is);
513 if (version < CONTENTFEATURES_VERSION)
514 throw SerializationError("unsupported ContentFeatures version");
517 name = deSerializeString16(is);
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;
525 param_type = (enum ContentParamType) readU8(is);
526 param_type_2 = (enum ContentParamType2) readU8(is);
529 drawtype = (enum NodeDrawType) readU8(is);
530 mesh = deSerializeString16(is);
531 visual_scale = readF32(is);
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);
543 color.setRed(readU8(is));
544 color.setGreen(readU8(is));
545 color.setBlue(readU8(is));
546 palette_name = deSerializeString16(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);
557 light_propagates = readU8(is);
558 sunlight_propagates = readU8(is);
559 light_source = readU8(is);
560 light_source = MYMIN(light_source, LIGHT_MAX);
563 is_ground_content = readU8(is);
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);
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);
585 node_box.deSerialize(is);
586 selection_box.deSerialize(is);
587 collision_box.deSerialize(is);
590 sound_footstep.deSerialize(is, version);
591 sound_dig.deSerialize(is, version);
592 sound_dug.deSerialize(is, version);
594 // read legacy properties
595 legacy_facedir_simple = readU8(is);
596 legacy_wallmounted = readU8(is);
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) {};
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)
613 layer->shader_id = shader_id;
614 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
615 layer->material_type = material_type;
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;
630 if (!tile.world_aligned)
633 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
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;
647 layer->has_color = tiledef.has_color;
648 if (tiledef.has_color)
649 layer->color = tiledef.color;
651 layer->color = color;
653 // Animation parameters
655 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
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;
663 if (frame_count == 1) {
664 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
666 std::ostringstream os(std::ios::binary);
667 if (!layer->frames) {
668 layer->frames = new std::vector<FrameSpec>();
670 layer->frames->resize(frame_count);
672 for (int i = 0; i < frame_count; i++) {
678 tiledef.animation.getTextureModifer(os,
679 layer->texture->getOriginalSize(), i);
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;
690 bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length)
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())
698 seen.insert(tiles[i].name);
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());
706 core::dimension2d<u32> dim = image->getDimension();
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) {
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;
737 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
739 if (style == ALIGN_STYLE_WORLD)
741 if (mode == WORLDALIGN_DISABLE)
743 if (style == ALIGN_STYLE_USER_DEFINED)
745 if (drawtype == NDT_NORMAL)
746 return mode >= WORLDALIGN_FORCE;
747 if (drawtype == NDT_NODEBOX)
748 return mode >= WORLDALIGN_FORCE_NODEBOX;
752 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
753 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
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);
759 // Figure out the actual tiles to use
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";
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];
775 bool is_liquid = false;
777 MaterialType material_type = (alpha == 255) ?
778 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
783 material_type = (alpha == 255) ?
784 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
791 assert(liquid_type == LIQUID_SOURCE);
792 if (tsettings.opaque_water)
797 case NDT_FLOWINGLIQUID:
798 assert(liquid_type == LIQUID_FLOWING);
800 if (tsettings.opaque_water)
806 visual_solidness = 1;
808 case NDT_GLASSLIKE_FRAMED:
810 visual_solidness = 1;
812 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
814 visual_solidness = 1;
815 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
819 visual_solidness = 1;
821 case NDT_ALLFACES_OPTIONAL:
822 if (tsettings.leaves_style == LEAVES_FANCY) {
823 drawtype = NDT_ALLFACES;
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;
831 drawtype = NDT_GLASSLIKE;
833 visual_solidness = 1;
835 drawtype = NDT_NORMAL;
837 for (TileDef &td : tdef)
838 td.name += std::string("^[noalpha");
841 material_type = TILE_MATERIAL_WAVING_LEAVES;
846 material_type = TILE_MATERIAL_WAVING_PLANTS;
853 if (alpha == 255 && textureAlphaCheck(tsrc, tdef, 6))
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;
873 case NDT_PLANTLIKE_ROOTED:
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);
885 material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE :
886 TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT;
888 material_type = (alpha == 255) ? TILE_MATERIAL_LIQUID_OPAQUE :
889 TILE_MATERIAL_LIQUID_TRANSPARENT;
893 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
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;
901 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
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);
916 MaterialType special_material = material_type;
917 if (drawtype == NDT_PLANTLIKE_ROOTED) {
919 special_material = TILE_MATERIAL_WAVING_PLANTS;
920 else if (waving == 2)
921 special_material = TILE_MATERIAL_WAVING_LEAVES;
923 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
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);
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);
936 if (drawtype == NDT_MESH && !mesh.empty()) {
938 // Read the mesh and apply scale
939 mesh_ptr[0] = client->getMesh(mesh);
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);
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);
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);
968 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
969 recalculateBoundingBox(mesh_ptr[0]);
970 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
982 NodeDefManager::NodeDefManager()
988 NodeDefManager::~NodeDefManager()
991 for (ContentFeatures &f : m_content_features) {
992 for (auto &j : f.mesh_ptr) {
1001 void NodeDefManager::clear()
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();
1008 m_selection_box_union.reset(0,0,0);
1009 m_selection_box_int_union.reset(0,0,0);
1011 resetNodeResolveState();
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);
1019 // Set CONTENT_UNKNOWN
1023 // Insert directly into containers
1024 content_t c = CONTENT_UNKNOWN;
1025 m_content_features[c] = f;
1026 addNameIdMapping(c, f.name);
1033 f.drawtype = NDT_AIRLIKE;
1034 f.param_type = CPT_LIGHT;
1035 f.light_propagates = true;
1036 f.sunlight_propagates = true;
1038 f.pointable = false;
1040 f.buildable_to = 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);
1049 // Set CONTENT_IGNORE
1053 f.drawtype = NDT_AIRLIKE;
1054 f.param_type = CPT_NONE;
1055 f.light_propagates = false;
1056 f.sunlight_propagates = false;
1058 f.pointable = 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);
1070 bool NodeDefManager::getId(const std::string &name, content_t &result) const
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())
1081 content_t NodeDefManager::getId(const std::string &name) const
1083 content_t id = CONTENT_IGNORE;
1089 bool NodeDefManager::getIds(const std::string &name,
1090 std::vector<content_t> &result) const
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);
1097 result.push_back(id);
1100 std::string group = name.substr(6);
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())
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());
1114 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1116 content_t id = CONTENT_UNKNOWN;
1122 // returns CONTENT_IGNORE if no free ID found
1123 content_t NodeDefManager::allocateId()
1125 for (content_t id = m_next_id;
1126 id >= m_next_id; // overflow?
1128 while (id >= m_content_features.size()) {
1129 m_content_features.emplace_back();
1131 const ContentFeatures &f = m_content_features[id];
1132 if (f.name.empty()) {
1137 // If we arrive here, an overflow occurred in id.
1138 // That means no ID was found
1139 return CONTENT_IGNORE;
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
1149 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1151 for (const aabb3f &box : boxes) {
1152 box_union->addInternalBox(box);
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
1163 * @param[in, out] box_union the union of the arguments
1165 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1168 switch(nodebox.type) {
1170 case NODEBOX_LEVELED: {
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;
1178 if (features.param_type_2 == CPT2_FACEDIR ||
1179 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1180 // Get maximal coordinate
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) };
1189 for (float coord : coords) {
1194 // Add the union of all possible rotated boxes
1195 box_union->addInternalPoint(-max, -max, -max);
1196 box_union->addInternalPoint(+max, +max, +max);
1198 box_union->addInternalBox(half_processed);
1202 case NODEBOX_WALLMOUNTED: {
1204 box_union->addInternalBox(nodebox.wall_top);
1205 box_union->addInternalBox(nodebox.wall_bottom);
1206 // Find maximal coordinate in the X-Z plane
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) };
1213 for (float coord : coords) {
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);
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);
1244 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1245 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1251 inline void NodeDefManager::fixSelectionBoxIntUnion()
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);
1268 void NodeDefManager::eraseIdFromGroups(content_t id)
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;
1276 // Remove any occurence of the id in the group items vector.
1277 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1279 // If group is empty, erase its vector from the map.
1281 iter_groups = m_group_to_items.erase(iter_groups);
1288 // IWritableNodeDefManager
1289 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1293 assert(name != "ignore");
1294 assert(name == def.name);
1296 content_t id = CONTENT_IGNORE;
1297 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1300 if (id == CONTENT_IGNORE) {
1301 warningstream << "NodeDefManager: Absolute "
1302 "limit reached" << std::endl;
1303 return CONTENT_IGNORE;
1305 assert(id != CONTENT_IGNORE);
1306 addNameIdMapping(id, name);
1309 // If there is already ContentFeatures registered for this id, clear old groups
1310 if (id < m_content_features.size())
1311 eraseIdFromGroups(id);
1313 m_content_features[id] = def;
1314 verbosestream << "NodeDefManager: registering content id \"" << id
1315 << "\": name=\"" << def.name << "\""<<std::endl;
1317 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1318 fixSelectionBoxIntUnion();
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);
1330 content_t NodeDefManager::allocateDummy(const std::string &name)
1332 assert(name != ""); // Pre-condition
1335 return set(name, f);
1339 void NodeDefManager::removeNode(const std::string &name)
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);
1351 eraseIdFromGroups(id);
1355 void NodeDefManager::updateAliases(IItemDefManager *idef)
1357 std::set<std::string> 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);
1363 if (m_name_id_mapping.getId(convert_to, id)) {
1364 m_name_id_mapping_with_aliases.insert(
1365 std::make_pair(name, id));
1370 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1372 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1373 "overrides to textures" << std::endl;
1375 for (const TextureOverride& texture_override : overrides) {
1377 if (!getId(texture_override.id, id))
1378 continue; // Ignore unknown node
1380 ContentFeatures &nodedef = m_content_features[id];
1383 if (texture_override.hasTarget(OverrideTarget::TOP))
1384 nodedef.tiledef[0].name = texture_override.texture;
1386 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1387 nodedef.tiledef[1].name = texture_override.texture;
1389 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1390 nodedef.tiledef[2].name = texture_override.texture;
1392 if (texture_override.hasTarget(OverrideTarget::LEFT))
1393 nodedef.tiledef[3].name = texture_override.texture;
1395 if (texture_override.hasTarget(OverrideTarget::BACK))
1396 nodedef.tiledef[4].name = texture_override.texture;
1398 if (texture_override.hasTarget(OverrideTarget::FRONT))
1399 nodedef.tiledef[5].name = texture_override.texture;
1402 // Override special tiles, if applicable
1403 if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1404 nodedef.tiledef_special[0].name = texture_override.texture;
1406 if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1407 nodedef.tiledef_special[1].name = texture_override.texture;
1409 if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1410 nodedef.tiledef_special[2].name = texture_override.texture;
1412 if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1413 nodedef.tiledef_special[3].name = texture_override.texture;
1415 if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1416 nodedef.tiledef_special[4].name = texture_override.texture;
1418 if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1419 nodedef.tiledef_special[5].name = texture_override.texture;
1423 void NodeDefManager::updateTextures(IGameDef *gamedef,
1424 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1425 void *progress_callback_args)
1428 infostream << "NodeDefManager::updateTextures(): Updating "
1429 "textures in node definitions" << std::endl;
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();
1439 u32 size = m_content_features.size();
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);
1449 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1451 writeU8(os, 1); // version
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)
1458 const ContentFeatures *f = &m_content_features[i];
1459 if (f->name.empty())
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());
1468 // must not overflow
1469 u16 next = count + 1;
1470 FATAL_ERROR_IF(next < count, "Overflow");
1473 writeU16(os, count);
1474 os << serializeString32(os2.str());
1478 void NodeDefManager::deSerialize(std::istream &is)
1481 int version = readU8(is);
1483 throw SerializationError("unsupported NodeDefinitionManager version");
1484 u16 count = readU16(is);
1485 std::istringstream is2(deSerializeString32(is), std::ios::binary);
1487 for (u16 n = 0; n < count; n++) {
1488 u16 i = readU16(is2);
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);
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;
1501 if (f.name.empty()) {
1502 warningstream << "NodeDefManager::deSerialize(): "
1503 "received empty name" << std::endl;
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;
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);
1522 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1523 fixSelectionBoxIntUnion();
1526 // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1527 // are not sent, resolve them client-side too.
1532 void NodeDefManager::addNameIdMapping(content_t i, std::string name)
1534 m_name_id_mapping.set(i, name);
1535 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1539 NodeDefManager *createNodeDefManager()
1541 return new NodeDefManager();
1545 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1548 if (m_node_registration_complete)
1549 nr->nodeResolveInternal();
1551 m_pending_resolve_callbacks.push_back(nr);
1555 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
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])
1563 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1564 m_pending_resolve_callbacks.resize(len);
1572 void NodeDefManager::runNodeResolveCallbacks()
1574 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1575 NodeResolver *nr = m_pending_resolve_callbacks[i];
1576 nr->nodeResolveInternal();
1579 m_pending_resolve_callbacks.clear();
1583 void NodeDefManager::resetNodeResolveState()
1585 m_node_registration_complete = false;
1586 m_pending_resolve_callbacks.clear();
1589 static void removeDupes(std::vector<content_t> &list)
1591 std::sort(list.begin(), list.end());
1592 auto new_end = std::unique(list.begin(), list.end());
1593 list.erase(new_end, list.end());
1596 void NodeDefManager::resolveCrossrefs()
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);
1604 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1607 for (const std::string &name : f.connects_to) {
1608 getIds(name, f.connects_to_ids);
1610 removeDupes(f.connects_to_ids);
1614 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1615 u8 connect_face) const
1617 const ContentFeatures &f1 = get(from);
1619 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1622 // lookup target in connected set
1623 if (!CONTAINS(f1.connects_to_ids, to.param0))
1626 const ContentFeatures &f2 = get(to);
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);
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,
1640 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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,
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
1649 return (f2.connect_sides
1650 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1652 return (f2.connect_sides & connect_face);
1654 // the target is just a regular node, so connect no matter back connection
1662 NodeResolver::NodeResolver()
1664 m_nodenames.reserve(16);
1665 m_nnlistsizes.reserve(4);
1669 NodeResolver::~NodeResolver()
1671 if (!m_resolve_done && m_ndef)
1672 m_ndef->cancelNodeResolveCallback(this);
1676 void NodeResolver::cloneTo(NodeResolver *res) const
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;
1688 void NodeResolver::nodeResolveInternal()
1690 m_nodenames_idx = 0;
1691 m_nnlistsizes_idx = 0;
1694 m_resolve_done = true;
1696 m_nodenames.clear();
1697 m_nnlistsizes.clear();
1701 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1702 const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1704 if (m_nodenames_idx == m_nodenames.size()) {
1705 *result_out = c_fallback;
1706 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1711 std::string name = m_nodenames[m_nodenames_idx++];
1713 bool success = m_ndef->getId(name, c);
1714 if (!success && !node_alt.empty()) {
1716 success = m_ndef->getId(name, c);
1720 if (error_on_fallback)
1721 errorstream << "NodeResolver: failed to resolve node name '" << name
1722 << "'." << std::endl;
1731 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1732 bool all_required, content_t c_fallback)
1734 bool success = true;
1736 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1737 errorstream << "NodeResolver: no more node lists" << std::endl;
1741 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1744 if (m_nodenames_idx == m_nodenames.size()) {
1745 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1750 std::string &name = m_nodenames[m_nodenames_idx++];
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);
1762 m_ndef->getIds(name, *result_out);