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)
363 alpha = ALPHAMODE_OPAQUE;
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";
407 liquid_move_physics = false;
410 void ContentFeatures::setAlphaFromLegacy(u8 legacy_alpha)
412 // No special handling for nodebox/mesh here as it doesn't make sense to
413 // throw warnings when the server is too old to support the "correct" way
416 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_CLIP;
419 case NDT_FLOWINGLIQUID:
420 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_BLEND;
423 alpha = legacy_alpha == 255 ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
428 u8 ContentFeatures::getAlphaForLegacy() const
430 // This is so simple only because 255 and 0 mean wildly different things
431 // depending on drawtype...
432 return alpha == ALPHAMODE_OPAQUE ? 255 : 0;
435 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
437 const u8 version = CONTENTFEATURES_VERSION;
438 writeU8(os, version);
441 os << serializeString16(name);
442 writeU16(os, groups.size());
443 for (const auto &group : groups) {
444 os << serializeString16(group.first);
445 writeS16(os, group.second);
447 writeU8(os, param_type);
448 writeU8(os, param_type_2);
451 writeU8(os, drawtype);
452 os << serializeString16(mesh);
453 writeF32(os, visual_scale);
455 for (const TileDef &td : tiledef)
456 td.serialize(os, protocol_version);
457 for (const TileDef &td : tiledef_overlay)
458 td.serialize(os, protocol_version);
459 writeU8(os, CF_SPECIAL_COUNT);
460 for (const TileDef &td : tiledef_special) {
461 td.serialize(os, protocol_version);
463 writeU8(os, getAlphaForLegacy());
464 writeU8(os, color.getRed());
465 writeU8(os, color.getGreen());
466 writeU8(os, color.getBlue());
467 os << serializeString16(palette_name);
469 writeU8(os, connect_sides);
470 writeU16(os, connects_to_ids.size());
471 for (u16 connects_to_id : connects_to_ids)
472 writeU16(os, connects_to_id);
473 writeARGB8(os, post_effect_color);
474 writeU8(os, leveled);
477 writeU8(os, light_propagates);
478 writeU8(os, sunlight_propagates);
479 writeU8(os, light_source);
482 writeU8(os, is_ground_content);
485 writeU8(os, walkable);
486 writeU8(os, pointable);
487 writeU8(os, diggable);
488 writeU8(os, climbable);
489 writeU8(os, buildable_to);
490 writeU8(os, rightclickable);
491 writeU32(os, damage_per_second);
494 writeU8(os, liquid_type);
495 os << serializeString16(liquid_alternative_flowing);
496 os << serializeString16(liquid_alternative_source);
497 writeU8(os, liquid_viscosity);
498 writeU8(os, liquid_renewable);
499 writeU8(os, liquid_range);
500 writeU8(os, drowning);
501 writeU8(os, floodable);
504 node_box.serialize(os, protocol_version);
505 selection_box.serialize(os, protocol_version);
506 collision_box.serialize(os, protocol_version);
509 sound_footstep.serialize(os, version);
510 sound_dig.serialize(os, version);
511 sound_dug.serialize(os, version);
514 writeU8(os, legacy_facedir_simple);
515 writeU8(os, legacy_wallmounted);
518 os << serializeString16(node_dig_prediction);
519 writeU8(os, leveled_max);
521 writeU8(os, move_resistance);
522 writeU8(os, liquid_move_physics);
525 void ContentFeatures::deSerialize(std::istream &is)
528 const u8 version = readU8(is);
529 if (version < CONTENTFEATURES_VERSION)
530 throw SerializationError("unsupported ContentFeatures version");
533 name = deSerializeString16(is);
535 u32 groups_size = readU16(is);
536 for (u32 i = 0; i < groups_size; i++) {
537 std::string name = deSerializeString16(is);
538 int value = readS16(is);
539 groups[name] = value;
541 param_type = (enum ContentParamType) readU8(is);
542 param_type_2 = (enum ContentParamType2) readU8(is);
545 drawtype = (enum NodeDrawType) readU8(is);
546 mesh = deSerializeString16(is);
547 visual_scale = readF32(is);
549 throw SerializationError("unsupported tile count");
550 for (TileDef &td : tiledef)
551 td.deSerialize(is, version, drawtype);
552 for (TileDef &td : tiledef_overlay)
553 td.deSerialize(is, version, drawtype);
554 if (readU8(is) != CF_SPECIAL_COUNT)
555 throw SerializationError("unsupported CF_SPECIAL_COUNT");
556 for (TileDef &td : tiledef_special)
557 td.deSerialize(is, version, drawtype);
558 setAlphaFromLegacy(readU8(is));
559 color.setRed(readU8(is));
560 color.setGreen(readU8(is));
561 color.setBlue(readU8(is));
562 palette_name = deSerializeString16(is);
564 connect_sides = readU8(is);
565 u16 connects_to_size = readU16(is);
566 connects_to_ids.clear();
567 for (u16 i = 0; i < connects_to_size; i++)
568 connects_to_ids.push_back(readU16(is));
569 post_effect_color = readARGB8(is);
570 leveled = readU8(is);
573 light_propagates = readU8(is);
574 sunlight_propagates = readU8(is);
575 light_source = readU8(is);
576 light_source = MYMIN(light_source, LIGHT_MAX);
579 is_ground_content = readU8(is);
582 walkable = readU8(is);
583 pointable = readU8(is);
584 diggable = readU8(is);
585 climbable = readU8(is);
586 buildable_to = readU8(is);
587 rightclickable = readU8(is);
588 damage_per_second = readU32(is);
591 liquid_type = (enum LiquidType) readU8(is);
592 liquid_move_physics = liquid_type != LIQUID_NONE;
593 liquid_alternative_flowing = deSerializeString16(is);
594 liquid_alternative_source = deSerializeString16(is);
595 liquid_viscosity = readU8(is);
596 move_resistance = liquid_viscosity; // set default move_resistance
597 liquid_renewable = readU8(is);
598 liquid_range = readU8(is);
599 drowning = readU8(is);
600 floodable = readU8(is);
603 node_box.deSerialize(is);
604 selection_box.deSerialize(is);
605 collision_box.deSerialize(is);
608 sound_footstep.deSerialize(is, version);
609 sound_dig.deSerialize(is, version);
610 sound_dug.deSerialize(is, version);
612 // read legacy properties
613 legacy_facedir_simple = readU8(is);
614 legacy_wallmounted = readU8(is);
617 node_dig_prediction = deSerializeString16(is);
620 if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
621 throw SerializationError("");
626 throw SerializationError("");
627 alpha = static_cast<enum AlphaMode>(tmp);
631 throw SerializationError("");
632 move_resistance = tmp;
636 throw SerializationError("");
637 liquid_move_physics = tmp;
638 } catch(SerializationError &e) {};
642 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
643 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
644 u8 material_type, u32 shader_id, bool backface_culling,
645 const TextureSettings &tsettings)
647 layer->shader_id = shader_id;
648 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
649 layer->material_type = material_type;
651 bool has_scale = tiledef.scale > 0;
652 bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
653 (tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
655 auto texture_size = layer->texture->getOriginalSize();
656 float base_size = tsettings.node_texture_size;
657 float size = std::fmin(texture_size.Width, texture_size.Height);
658 layer->scale = std::fmax(base_size, size) / base_size;
659 } else if (has_scale) {
660 layer->scale = tiledef.scale;
664 if (!tile.world_aligned)
667 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
670 layer->material_flags = 0;
671 if (backface_culling)
672 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
673 if (tiledef.animation.type != TAT_NONE)
674 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
675 if (tiledef.tileable_horizontal)
676 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
677 if (tiledef.tileable_vertical)
678 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
681 layer->has_color = tiledef.has_color;
682 if (tiledef.has_color)
683 layer->color = tiledef.color;
685 layer->color = color;
687 // Animation parameters
689 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
691 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
692 &frame_count, &frame_length_ms, NULL);
693 layer->animation_frame_count = frame_count;
694 layer->animation_frame_length_ms = frame_length_ms;
697 if (frame_count == 1) {
698 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
700 std::ostringstream os(std::ios::binary);
701 if (!layer->frames) {
702 layer->frames = new std::vector<FrameSpec>();
704 layer->frames->resize(frame_count);
706 for (int i = 0; i < frame_count; i++) {
712 tiledef.animation.getTextureModifer(os,
713 layer->texture->getOriginalSize(), i);
715 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
716 if (layer->normal_texture)
717 frame.normal_texture = tsrc->getNormalTexture(os.str());
718 frame.flags_texture = layer->flags_texture;
719 (*layer->frames)[i] = frame;
724 bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length)
726 video::IVideoDriver *driver = RenderingEngine::get_video_driver();
727 static thread_local bool long_warning_printed = false;
728 std::set<std::string> seen;
730 for (int i = 0; i < length; i++) {
731 if (seen.find(tiles[i].name) != seen.end())
733 seen.insert(tiles[i].name);
735 // Load the texture and see if there's any transparent pixels
736 video::ITexture *texture = tsrc->getTexture(tiles[i].name);
737 video::IImage *image = driver->createImage(texture,
738 core::position2d<s32>(0, 0), texture->getOriginalSize());
741 core::dimension2d<u32> dim = image->getDimension();
743 for (u16 x = 0; x < dim.Width; x++) {
744 for (u16 y = 0; y < dim.Height; y++) {
745 if (image->getPixel(x, y).getAlpha() < 255) {
756 warningstream << "Texture \"" << tiles[i].name << "\" of "
757 << name << " has transparency, assuming "
758 "use_texture_alpha = \"clip\"." << std::endl;
759 if (!long_warning_printed) {
760 warningstream << " This warning can be a false-positive if "
761 "unused pixels in the texture are transparent. However if "
762 "it is meant to be transparent, you *MUST* update the "
763 "nodedef and set use_texture_alpha = \"clip\"! This "
764 "compatibility code will be removed in a few releases."
766 long_warning_printed = true;
773 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
775 if (style == ALIGN_STYLE_WORLD)
777 if (mode == WORLDALIGN_DISABLE)
779 if (style == ALIGN_STYLE_USER_DEFINED)
781 if (drawtype == NDT_NORMAL)
782 return mode >= WORLDALIGN_FORCE;
783 if (drawtype == NDT_NODEBOX)
784 return mode >= WORLDALIGN_FORCE_NODEBOX;
788 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
789 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
791 // minimap pixel color - the average color of a texture
792 if (tsettings.enable_minimap && !tiledef[0].name.empty())
793 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
795 // Figure out the actual tiles to use
797 for (u32 j = 0; j < 6; j++) {
798 tdef[j] = tiledef[j];
799 if (tdef[j].name.empty())
800 tdef[j].name = "unknown_node.png";
802 // also the overlay tiles
803 TileDef tdef_overlay[6];
804 for (u32 j = 0; j < 6; j++)
805 tdef_overlay[j] = tiledef_overlay[j];
806 // also the special tiles
807 TileDef tdef_spec[6];
808 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
809 tdef_spec[j] = tiledef_special[j];
811 bool is_liquid = false;
813 if (alpha == ALPHAMODE_LEGACY_COMPAT) {
814 // Before working with the alpha mode, resolve any legacy kludges
815 alpha = textureAlphaCheck(tsrc, tdef, 6) ? ALPHAMODE_CLIP : ALPHAMODE_OPAQUE;
818 MaterialType material_type = alpha == ALPHAMODE_OPAQUE ?
819 TILE_MATERIAL_OPAQUE : (alpha == ALPHAMODE_CLIP ? TILE_MATERIAL_BASIC :
820 TILE_MATERIAL_ALPHA);
831 if (tsettings.opaque_water)
832 alpha = ALPHAMODE_OPAQUE;
836 case NDT_FLOWINGLIQUID:
838 if (tsettings.opaque_water)
839 alpha = ALPHAMODE_OPAQUE;
844 visual_solidness = 1;
846 case NDT_GLASSLIKE_FRAMED:
848 visual_solidness = 1;
850 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
852 visual_solidness = 1;
853 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
857 visual_solidness = 1;
859 case NDT_ALLFACES_OPTIONAL:
860 if (tsettings.leaves_style == LEAVES_FANCY) {
861 drawtype = NDT_ALLFACES;
863 visual_solidness = 1;
864 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
865 for (u32 j = 0; j < 6; j++) {
866 if (!tdef_spec[j].name.empty())
867 tdef[j].name = tdef_spec[j].name;
869 drawtype = NDT_GLASSLIKE;
871 visual_solidness = 1;
873 drawtype = NDT_NORMAL;
875 for (TileDef &td : tdef)
876 td.name += std::string("^[noalpha");
879 material_type = TILE_MATERIAL_WAVING_LEAVES;
884 material_type = TILE_MATERIAL_WAVING_PLANTS;
893 material_type = TILE_MATERIAL_WAVING_PLANTS;
894 } else if (waving == 2) {
895 material_type = TILE_MATERIAL_WAVING_LEAVES;
896 } else if (waving == 3) {
897 material_type = alpha == ALPHAMODE_OPAQUE ?
898 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
899 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
908 case NDT_PLANTLIKE_ROOTED:
915 material_type = alpha == ALPHAMODE_OPAQUE ?
916 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
917 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
919 material_type = alpha == ALPHAMODE_OPAQUE ? TILE_MATERIAL_LIQUID_OPAQUE :
920 TILE_MATERIAL_LIQUID_TRANSPARENT;
924 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
926 MaterialType overlay_material = material_type;
927 if (overlay_material == TILE_MATERIAL_OPAQUE)
928 overlay_material = TILE_MATERIAL_BASIC;
929 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
930 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
932 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
934 // Tiles (fill in f->tiles[])
935 for (u16 j = 0; j < 6; j++) {
936 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
937 tsettings.world_aligned_mode, drawtype);
938 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
939 color, material_type, tile_shader,
940 tdef[j].backface_culling, tsettings);
941 if (!tdef_overlay[j].name.empty())
942 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
943 color, overlay_material, overlay_shader,
944 tdef[j].backface_culling, tsettings);
947 MaterialType special_material = material_type;
948 if (drawtype == NDT_PLANTLIKE_ROOTED) {
950 special_material = TILE_MATERIAL_WAVING_PLANTS;
951 else if (waving == 2)
952 special_material = TILE_MATERIAL_WAVING_LEAVES;
954 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
956 // Special tiles (fill in f->special_tiles[])
957 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
958 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
959 color, special_material, special_shader,
960 tdef_spec[j].backface_culling, tsettings);
962 if (param_type_2 == CPT2_COLOR ||
963 param_type_2 == CPT2_COLORED_FACEDIR ||
964 param_type_2 == CPT2_COLORED_WALLMOUNTED ||
965 param_type_2 == CPT2_COLORED_DEGROTATE)
966 palette = tsrc->getPalette(palette_name);
968 if (drawtype == NDT_MESH && !mesh.empty()) {
970 // Read the mesh and apply scale
971 mesh_ptr[0] = client->getMesh(mesh);
973 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
974 scaleMesh(mesh_ptr[0], scale);
975 recalculateBoundingBox(mesh_ptr[0]);
976 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
980 //Cache 6dfacedir and wallmounted rotated clones of meshes
981 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
982 (param_type_2 == CPT2_FACEDIR
983 || param_type_2 == CPT2_COLORED_FACEDIR)) {
984 for (u16 j = 1; j < 24; j++) {
985 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
986 rotateMeshBy6dFacedir(mesh_ptr[j], j);
987 recalculateBoundingBox(mesh_ptr[j]);
988 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
990 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
991 && (param_type_2 == CPT2_WALLMOUNTED ||
992 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
993 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
994 for (u16 j = 1; j < 6; j++) {
995 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
996 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
997 recalculateBoundingBox(mesh_ptr[j]);
998 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
1000 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
1001 recalculateBoundingBox(mesh_ptr[0]);
1002 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
1014 NodeDefManager::NodeDefManager()
1020 NodeDefManager::~NodeDefManager()
1023 for (ContentFeatures &f : m_content_features) {
1024 for (auto &j : f.mesh_ptr) {
1033 void NodeDefManager::clear()
1035 m_content_features.clear();
1036 m_name_id_mapping.clear();
1037 m_name_id_mapping_with_aliases.clear();
1038 m_group_to_items.clear();
1040 m_selection_box_union.reset(0,0,0);
1041 m_selection_box_int_union.reset(0,0,0);
1043 resetNodeResolveState();
1045 u32 initial_length = 0;
1046 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1047 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1048 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1049 m_content_features.resize(initial_length);
1051 // Set CONTENT_UNKNOWN
1055 // Insert directly into containers
1056 content_t c = CONTENT_UNKNOWN;
1057 m_content_features[c] = f;
1058 addNameIdMapping(c, f.name);
1065 f.drawtype = NDT_AIRLIKE;
1066 f.param_type = CPT_LIGHT;
1067 f.light_propagates = true;
1068 f.sunlight_propagates = true;
1070 f.pointable = false;
1072 f.buildable_to = true;
1074 f.is_ground_content = true;
1075 // Insert directly into containers
1076 content_t c = CONTENT_AIR;
1077 m_content_features[c] = f;
1078 addNameIdMapping(c, f.name);
1081 // Set CONTENT_IGNORE
1085 f.drawtype = NDT_AIRLIKE;
1086 f.param_type = CPT_NONE;
1087 f.light_propagates = false;
1088 f.sunlight_propagates = false;
1090 f.pointable = false;
1092 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1093 f.is_ground_content = true;
1094 // Insert directly into containers
1095 content_t c = CONTENT_IGNORE;
1096 m_content_features[c] = f;
1097 addNameIdMapping(c, f.name);
1102 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1104 std::unordered_map<std::string, content_t>::const_iterator
1105 i = m_name_id_mapping_with_aliases.find(name);
1106 if(i == m_name_id_mapping_with_aliases.end())
1113 content_t NodeDefManager::getId(const std::string &name) const
1115 content_t id = CONTENT_IGNORE;
1121 bool NodeDefManager::getIds(const std::string &name,
1122 std::vector<content_t> &result) const
1124 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1125 if (name.substr(0,6) != "group:") {
1126 content_t id = CONTENT_IGNORE;
1127 bool exists = getId(name, id);
1129 result.push_back(id);
1132 std::string group = name.substr(6);
1134 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1135 i = m_group_to_items.find(group);
1136 if (i == m_group_to_items.end())
1139 const std::vector<content_t> &items = i->second;
1140 result.insert(result.end(), items.begin(), items.end());
1141 //printf("getIds: %dus\n", t.stop());
1146 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1148 content_t id = CONTENT_UNKNOWN;
1154 // returns CONTENT_IGNORE if no free ID found
1155 content_t NodeDefManager::allocateId()
1157 for (content_t id = m_next_id;
1158 id >= m_next_id; // overflow?
1160 while (id >= m_content_features.size()) {
1161 m_content_features.emplace_back();
1163 const ContentFeatures &f = m_content_features[id];
1164 if (f.name.empty()) {
1169 // If we arrive here, an overflow occurred in id.
1170 // That means no ID was found
1171 return CONTENT_IGNORE;
1176 * Returns the smallest box that contains all boxes
1177 * in the vector. Box_union is expanded.
1178 * @param[in] boxes the vector containing the boxes
1179 * @param[in, out] box_union the union of the arguments
1181 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1183 for (const aabb3f &box : boxes) {
1184 box_union->addInternalBox(box);
1190 * Returns a box that contains the nodebox in every case.
1191 * The argument node_union is expanded.
1192 * @param[in] nodebox the nodebox to be measured
1193 * @param[in] features used to decide whether the nodebox
1195 * @param[in, out] box_union the union of the arguments
1197 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1200 switch(nodebox.type) {
1202 case NODEBOX_LEVELED: {
1204 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1205 boxVectorUnion(nodebox.fixed, &half_processed);
1206 // Set leveled boxes to maximal
1207 if (nodebox.type == NODEBOX_LEVELED) {
1208 half_processed.MaxEdge.Y = +BS / 2;
1210 if (features.param_type_2 == CPT2_FACEDIR ||
1211 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1212 // Get maximal coordinate
1214 fabsf(half_processed.MinEdge.X),
1215 fabsf(half_processed.MinEdge.Y),
1216 fabsf(half_processed.MinEdge.Z),
1217 fabsf(half_processed.MaxEdge.X),
1218 fabsf(half_processed.MaxEdge.Y),
1219 fabsf(half_processed.MaxEdge.Z) };
1221 for (float coord : coords) {
1226 // Add the union of all possible rotated boxes
1227 box_union->addInternalPoint(-max, -max, -max);
1228 box_union->addInternalPoint(+max, +max, +max);
1230 box_union->addInternalBox(half_processed);
1234 case NODEBOX_WALLMOUNTED: {
1236 box_union->addInternalBox(nodebox.wall_top);
1237 box_union->addInternalBox(nodebox.wall_bottom);
1238 // Find maximal coordinate in the X-Z plane
1240 fabsf(nodebox.wall_side.MinEdge.X),
1241 fabsf(nodebox.wall_side.MinEdge.Z),
1242 fabsf(nodebox.wall_side.MaxEdge.X),
1243 fabsf(nodebox.wall_side.MaxEdge.Z) };
1245 for (float coord : coords) {
1250 // Add the union of all possible rotated boxes
1251 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1252 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1255 case NODEBOX_CONNECTED: {
1256 // Add all possible connected boxes
1257 boxVectorUnion(nodebox.fixed, box_union);
1258 boxVectorUnion(nodebox.connect_top, box_union);
1259 boxVectorUnion(nodebox.connect_bottom, box_union);
1260 boxVectorUnion(nodebox.connect_front, box_union);
1261 boxVectorUnion(nodebox.connect_left, box_union);
1262 boxVectorUnion(nodebox.connect_back, box_union);
1263 boxVectorUnion(nodebox.connect_right, box_union);
1264 boxVectorUnion(nodebox.disconnected_top, box_union);
1265 boxVectorUnion(nodebox.disconnected_bottom, box_union);
1266 boxVectorUnion(nodebox.disconnected_front, box_union);
1267 boxVectorUnion(nodebox.disconnected_left, box_union);
1268 boxVectorUnion(nodebox.disconnected_back, box_union);
1269 boxVectorUnion(nodebox.disconnected_right, box_union);
1270 boxVectorUnion(nodebox.disconnected, box_union);
1271 boxVectorUnion(nodebox.disconnected_sides, box_union);
1276 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1277 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1283 inline void NodeDefManager::fixSelectionBoxIntUnion()
1285 m_selection_box_int_union.MinEdge.X = floorf(
1286 m_selection_box_union.MinEdge.X / BS + 0.5f);
1287 m_selection_box_int_union.MinEdge.Y = floorf(
1288 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1289 m_selection_box_int_union.MinEdge.Z = floorf(
1290 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1291 m_selection_box_int_union.MaxEdge.X = ceilf(
1292 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1293 m_selection_box_int_union.MaxEdge.Y = ceilf(
1294 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1295 m_selection_box_int_union.MaxEdge.Z = ceilf(
1296 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1300 void NodeDefManager::eraseIdFromGroups(content_t id)
1302 // For all groups in m_group_to_items...
1303 for (auto iter_groups = m_group_to_items.begin();
1304 iter_groups != m_group_to_items.end();) {
1305 // Get the group items vector.
1306 std::vector<content_t> &items = iter_groups->second;
1308 // Remove any occurence of the id in the group items vector.
1309 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1311 // If group is empty, erase its vector from the map.
1313 iter_groups = m_group_to_items.erase(iter_groups);
1320 // IWritableNodeDefManager
1321 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1325 assert(name != "ignore");
1326 assert(name == def.name);
1328 content_t id = CONTENT_IGNORE;
1329 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1332 if (id == CONTENT_IGNORE) {
1333 warningstream << "NodeDefManager: Absolute "
1334 "limit reached" << std::endl;
1335 return CONTENT_IGNORE;
1337 assert(id != CONTENT_IGNORE);
1338 addNameIdMapping(id, name);
1341 // If there is already ContentFeatures registered for this id, clear old groups
1342 if (id < m_content_features.size())
1343 eraseIdFromGroups(id);
1345 m_content_features[id] = def;
1346 verbosestream << "NodeDefManager: registering content id \"" << id
1347 << "\": name=\"" << def.name << "\""<<std::endl;
1349 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1350 fixSelectionBoxIntUnion();
1352 // Add this content to the list of all groups it belongs to
1353 for (const auto &group : def.groups) {
1354 const std::string &group_name = group.first;
1355 m_group_to_items[group_name].push_back(id);
1362 content_t NodeDefManager::allocateDummy(const std::string &name)
1364 assert(name != ""); // Pre-condition
1367 return set(name, f);
1371 void NodeDefManager::removeNode(const std::string &name)
1376 // Erase name from name ID mapping
1377 content_t id = CONTENT_IGNORE;
1378 if (m_name_id_mapping.getId(name, id)) {
1379 m_name_id_mapping.eraseName(name);
1380 m_name_id_mapping_with_aliases.erase(name);
1383 eraseIdFromGroups(id);
1387 void NodeDefManager::updateAliases(IItemDefManager *idef)
1389 std::set<std::string> all;
1391 m_name_id_mapping_with_aliases.clear();
1392 for (const std::string &name : all) {
1393 const std::string &convert_to = idef->getAlias(name);
1395 if (m_name_id_mapping.getId(convert_to, id)) {
1396 m_name_id_mapping_with_aliases.insert(
1397 std::make_pair(name, id));
1402 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1404 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1405 "overrides to textures" << std::endl;
1407 for (const TextureOverride& texture_override : overrides) {
1409 if (!getId(texture_override.id, id))
1410 continue; // Ignore unknown node
1412 ContentFeatures &nodedef = m_content_features[id];
1415 if (texture_override.hasTarget(OverrideTarget::TOP))
1416 nodedef.tiledef[0].name = texture_override.texture;
1418 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1419 nodedef.tiledef[1].name = texture_override.texture;
1421 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1422 nodedef.tiledef[2].name = texture_override.texture;
1424 if (texture_override.hasTarget(OverrideTarget::LEFT))
1425 nodedef.tiledef[3].name = texture_override.texture;
1427 if (texture_override.hasTarget(OverrideTarget::BACK))
1428 nodedef.tiledef[4].name = texture_override.texture;
1430 if (texture_override.hasTarget(OverrideTarget::FRONT))
1431 nodedef.tiledef[5].name = texture_override.texture;
1434 // Override special tiles, if applicable
1435 if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1436 nodedef.tiledef_special[0].name = texture_override.texture;
1438 if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1439 nodedef.tiledef_special[1].name = texture_override.texture;
1441 if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1442 nodedef.tiledef_special[2].name = texture_override.texture;
1444 if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1445 nodedef.tiledef_special[3].name = texture_override.texture;
1447 if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1448 nodedef.tiledef_special[4].name = texture_override.texture;
1450 if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1451 nodedef.tiledef_special[5].name = texture_override.texture;
1455 void NodeDefManager::updateTextures(IGameDef *gamedef, void *progress_callback_args)
1458 infostream << "NodeDefManager::updateTextures(): Updating "
1459 "textures in node definitions" << std::endl;
1461 Client *client = (Client *)gamedef;
1462 ITextureSource *tsrc = client->tsrc();
1463 IShaderSource *shdsrc = client->getShaderSource();
1464 auto smgr = client->getSceneManager();
1465 scene::IMeshManipulator *meshmanip = smgr->getMeshManipulator();
1466 TextureSettings tsettings;
1467 tsettings.readSettings();
1469 u32 size = m_content_features.size();
1471 for (u32 i = 0; i < size; i++) {
1472 ContentFeatures *f = &(m_content_features[i]);
1473 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1474 client->showUpdateProgressTexture(progress_callback_args, i, size);
1479 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1481 writeU8(os, 1); // version
1483 std::ostringstream os2(std::ios::binary);
1484 for (u32 i = 0; i < m_content_features.size(); i++) {
1485 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1486 || i == CONTENT_UNKNOWN)
1488 const ContentFeatures *f = &m_content_features[i];
1489 if (f->name.empty())
1492 // Wrap it in a string to allow different lengths without
1493 // strict version incompatibilities
1494 std::ostringstream wrapper_os(std::ios::binary);
1495 f->serialize(wrapper_os, protocol_version);
1496 os2<<serializeString16(wrapper_os.str());
1498 // must not overflow
1499 u16 next = count + 1;
1500 FATAL_ERROR_IF(next < count, "Overflow");
1503 writeU16(os, count);
1504 os << serializeString32(os2.str());
1508 void NodeDefManager::deSerialize(std::istream &is)
1511 int version = readU8(is);
1513 throw SerializationError("unsupported NodeDefinitionManager version");
1514 u16 count = readU16(is);
1515 std::istringstream is2(deSerializeString32(is), std::ios::binary);
1517 for (u16 n = 0; n < count; n++) {
1518 u16 i = readU16(is2);
1520 // Read it from the string wrapper
1521 std::string wrapper = deSerializeString16(is2);
1522 std::istringstream wrapper_is(wrapper, std::ios::binary);
1523 f.deSerialize(wrapper_is);
1525 // Check error conditions
1526 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1527 warningstream << "NodeDefManager::deSerialize(): "
1528 "not changing builtin node " << i << std::endl;
1531 if (f.name.empty()) {
1532 warningstream << "NodeDefManager::deSerialize(): "
1533 "received empty name" << std::endl;
1539 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1540 warningstream << "NodeDefManager::deSerialize(): "
1541 "already defined with different ID: " << f.name << std::endl;
1545 // All is ok, add node definition with the requested ID
1546 if (i >= m_content_features.size())
1547 m_content_features.resize((u32)(i) + 1);
1548 m_content_features[i] = f;
1549 addNameIdMapping(i, f.name);
1550 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1552 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1553 fixSelectionBoxIntUnion();
1556 // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1557 // are not sent, resolve them client-side too.
1562 void NodeDefManager::addNameIdMapping(content_t i, const std::string &name)
1564 m_name_id_mapping.set(i, name);
1565 m_name_id_mapping_with_aliases.emplace(name, i);
1569 NodeDefManager *createNodeDefManager()
1571 return new NodeDefManager();
1575 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1578 if (m_node_registration_complete)
1579 nr->nodeResolveInternal();
1581 m_pending_resolve_callbacks.push_back(nr);
1585 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1587 size_t len = m_pending_resolve_callbacks.size();
1588 for (size_t i = 0; i != len; i++) {
1589 if (nr != m_pending_resolve_callbacks[i])
1593 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1594 m_pending_resolve_callbacks.resize(len);
1602 void NodeDefManager::runNodeResolveCallbacks()
1604 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1605 NodeResolver *nr = m_pending_resolve_callbacks[i];
1606 nr->nodeResolveInternal();
1609 m_pending_resolve_callbacks.clear();
1613 void NodeDefManager::resetNodeResolveState()
1615 m_node_registration_complete = false;
1616 m_pending_resolve_callbacks.clear();
1619 static void removeDupes(std::vector<content_t> &list)
1621 std::sort(list.begin(), list.end());
1622 auto new_end = std::unique(list.begin(), list.end());
1623 list.erase(new_end, list.end());
1626 void NodeDefManager::resolveCrossrefs()
1628 for (ContentFeatures &f : m_content_features) {
1629 if (f.liquid_type != LIQUID_NONE || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
1630 f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1631 f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1634 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1637 for (const std::string &name : f.connects_to) {
1638 getIds(name, f.connects_to_ids);
1640 removeDupes(f.connects_to_ids);
1644 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1645 u8 connect_face) const
1647 const ContentFeatures &f1 = get(from);
1649 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1652 // lookup target in connected set
1653 if (!CONTAINS(f1.connects_to_ids, to.param0))
1656 const ContentFeatures &f2 = get(to);
1658 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1659 // ignores actually looking if back connection exists
1660 return CONTAINS(f2.connects_to_ids, from.param0);
1662 // does to node declare usable faces?
1663 if (f2.connect_sides > 0) {
1664 if ((f2.param_type_2 == CPT2_FACEDIR ||
1665 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1666 && (connect_face >= 4)) {
1667 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1668 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1670 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1672 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1673 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1675 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1676 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1677 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1679 return (f2.connect_sides
1680 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1682 return (f2.connect_sides & connect_face);
1684 // the target is just a regular node, so connect no matter back connection
1692 NodeResolver::NodeResolver()
1698 NodeResolver::~NodeResolver()
1700 if (!m_resolve_done && m_ndef)
1701 m_ndef->cancelNodeResolveCallback(this);
1705 void NodeResolver::cloneTo(NodeResolver *res) const
1707 FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1708 " after resolving has completed");
1709 /* We don't actually do anything significant. Since the node resolving has
1710 * already completed, the class that called us will already have the
1711 * resolved IDs in its data structures (which it copies on its own) */
1712 res->m_ndef = m_ndef;
1713 res->m_resolve_done = true;
1717 void NodeResolver::nodeResolveInternal()
1719 m_nodenames_idx = 0;
1720 m_nnlistsizes_idx = 0;
1723 m_resolve_done = true;
1725 m_nodenames.clear();
1726 m_nnlistsizes.clear();
1730 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1731 const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1733 if (m_nodenames_idx == m_nodenames.size()) {
1734 *result_out = c_fallback;
1735 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1740 std::string name = m_nodenames[m_nodenames_idx++];
1742 bool success = m_ndef->getId(name, c);
1743 if (!success && !node_alt.empty()) {
1745 success = m_ndef->getId(name, c);
1749 if (error_on_fallback)
1750 errorstream << "NodeResolver: failed to resolve node name '" << name
1751 << "'." << std::endl;
1760 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1761 bool all_required, content_t c_fallback)
1763 bool success = true;
1765 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1766 errorstream << "NodeResolver: no more node lists" << std::endl;
1770 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1773 if (m_nodenames_idx == m_nodenames.size()) {
1774 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1779 std::string &name = m_nodenames[m_nodenames_idx++];
1781 if (name.substr(0,6) != "group:") {
1782 if (m_ndef->getId(name, c)) {
1783 result_out->push_back(c);
1784 } else if (all_required) {
1785 errorstream << "NodeResolver: failed to resolve node name '"
1786 << name << "'." << std::endl;
1787 result_out->push_back(c_fallback);
1791 m_ndef->getIds(name, *result_out);
1798 void NodeResolver::reset(bool resolve_done)
1800 m_nodenames.clear();
1801 m_nodenames_idx = 0;
1802 m_nnlistsizes.clear();
1803 m_nnlistsizes_idx = 0;
1805 m_resolve_done = resolve_done;
1807 m_nodenames.reserve(16);
1808 m_nnlistsizes.reserve(4);