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";
408 void ContentFeatures::setAlphaFromLegacy(u8 legacy_alpha)
410 // No special handling for nodebox/mesh here as it doesn't make sense to
411 // throw warnings when the server is too old to support the "correct" way
414 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_CLIP;
417 case NDT_FLOWINGLIQUID:
418 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_BLEND;
421 alpha = legacy_alpha == 255 ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
426 u8 ContentFeatures::getAlphaForLegacy() const
428 // This is so simple only because 255 and 0 mean wildly different things
429 // depending on drawtype...
430 return alpha == ALPHAMODE_OPAQUE ? 255 : 0;
433 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
435 const u8 version = CONTENTFEATURES_VERSION;
436 writeU8(os, version);
439 os << serializeString16(name);
440 writeU16(os, groups.size());
441 for (const auto &group : groups) {
442 os << serializeString16(group.first);
443 writeS16(os, group.second);
445 writeU8(os, param_type);
446 writeU8(os, param_type_2);
449 writeU8(os, drawtype);
450 os << serializeString16(mesh);
451 writeF32(os, visual_scale);
453 for (const TileDef &td : tiledef)
454 td.serialize(os, protocol_version);
455 for (const TileDef &td : tiledef_overlay)
456 td.serialize(os, protocol_version);
457 writeU8(os, CF_SPECIAL_COUNT);
458 for (const TileDef &td : tiledef_special) {
459 td.serialize(os, protocol_version);
461 writeU8(os, getAlphaForLegacy());
462 writeU8(os, color.getRed());
463 writeU8(os, color.getGreen());
464 writeU8(os, color.getBlue());
465 os << serializeString16(palette_name);
467 writeU8(os, connect_sides);
468 writeU16(os, connects_to_ids.size());
469 for (u16 connects_to_id : connects_to_ids)
470 writeU16(os, connects_to_id);
471 writeARGB8(os, post_effect_color);
472 writeU8(os, leveled);
475 writeU8(os, light_propagates);
476 writeU8(os, sunlight_propagates);
477 writeU8(os, light_source);
480 writeU8(os, is_ground_content);
483 writeU8(os, walkable);
484 writeU8(os, pointable);
485 writeU8(os, diggable);
486 writeU8(os, climbable);
487 writeU8(os, buildable_to);
488 writeU8(os, rightclickable);
489 writeU32(os, damage_per_second);
492 writeU8(os, liquid_type);
493 os << serializeString16(liquid_alternative_flowing);
494 os << serializeString16(liquid_alternative_source);
495 writeU8(os, liquid_viscosity);
496 writeU8(os, liquid_renewable);
497 writeU8(os, liquid_range);
498 writeU8(os, drowning);
499 writeU8(os, floodable);
502 node_box.serialize(os, protocol_version);
503 selection_box.serialize(os, protocol_version);
504 collision_box.serialize(os, protocol_version);
507 sound_footstep.serialize(os, version);
508 sound_dig.serialize(os, version);
509 sound_dug.serialize(os, version);
512 writeU8(os, legacy_facedir_simple);
513 writeU8(os, legacy_wallmounted);
515 os << serializeString16(node_dig_prediction);
516 writeU8(os, leveled_max);
520 void ContentFeatures::deSerialize(std::istream &is)
523 const u8 version = readU8(is);
524 if (version < CONTENTFEATURES_VERSION)
525 throw SerializationError("unsupported ContentFeatures version");
528 name = deSerializeString16(is);
530 u32 groups_size = readU16(is);
531 for (u32 i = 0; i < groups_size; i++) {
532 std::string name = deSerializeString16(is);
533 int value = readS16(is);
534 groups[name] = value;
536 param_type = (enum ContentParamType) readU8(is);
537 param_type_2 = (enum ContentParamType2) readU8(is);
540 drawtype = (enum NodeDrawType) readU8(is);
541 mesh = deSerializeString16(is);
542 visual_scale = readF32(is);
544 throw SerializationError("unsupported tile count");
545 for (TileDef &td : tiledef)
546 td.deSerialize(is, version, drawtype);
547 for (TileDef &td : tiledef_overlay)
548 td.deSerialize(is, version, drawtype);
549 if (readU8(is) != CF_SPECIAL_COUNT)
550 throw SerializationError("unsupported CF_SPECIAL_COUNT");
551 for (TileDef &td : tiledef_special)
552 td.deSerialize(is, version, drawtype);
553 setAlphaFromLegacy(readU8(is));
554 color.setRed(readU8(is));
555 color.setGreen(readU8(is));
556 color.setBlue(readU8(is));
557 palette_name = deSerializeString16(is);
559 connect_sides = readU8(is);
560 u16 connects_to_size = readU16(is);
561 connects_to_ids.clear();
562 for (u16 i = 0; i < connects_to_size; i++)
563 connects_to_ids.push_back(readU16(is));
564 post_effect_color = readARGB8(is);
565 leveled = readU8(is);
568 light_propagates = readU8(is);
569 sunlight_propagates = readU8(is);
570 light_source = readU8(is);
571 light_source = MYMIN(light_source, LIGHT_MAX);
574 is_ground_content = readU8(is);
577 walkable = readU8(is);
578 pointable = readU8(is);
579 diggable = readU8(is);
580 climbable = readU8(is);
581 buildable_to = readU8(is);
582 rightclickable = readU8(is);
583 damage_per_second = readU32(is);
586 liquid_type = (enum LiquidType) readU8(is);
587 liquid_alternative_flowing = deSerializeString16(is);
588 liquid_alternative_source = deSerializeString16(is);
589 liquid_viscosity = readU8(is);
590 liquid_renewable = readU8(is);
591 liquid_range = readU8(is);
592 drowning = readU8(is);
593 floodable = readU8(is);
596 node_box.deSerialize(is);
597 selection_box.deSerialize(is);
598 collision_box.deSerialize(is);
601 sound_footstep.deSerialize(is, version);
602 sound_dig.deSerialize(is, version);
603 sound_dug.deSerialize(is, version);
605 // read legacy properties
606 legacy_facedir_simple = readU8(is);
607 legacy_wallmounted = readU8(is);
610 node_dig_prediction = deSerializeString16(is);
613 if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
614 throw SerializationError("");
619 throw SerializationError("");
620 alpha = static_cast<enum AlphaMode>(tmp);
621 } catch(SerializationError &e) {};
625 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
626 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
627 u8 material_type, u32 shader_id, bool backface_culling,
628 const TextureSettings &tsettings)
630 layer->shader_id = shader_id;
631 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
632 layer->material_type = material_type;
634 bool has_scale = tiledef.scale > 0;
635 bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
636 (tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
638 auto texture_size = layer->texture->getOriginalSize();
639 float base_size = tsettings.node_texture_size;
640 float size = std::fmin(texture_size.Width, texture_size.Height);
641 layer->scale = std::fmax(base_size, size) / base_size;
642 } else if (has_scale) {
643 layer->scale = tiledef.scale;
647 if (!tile.world_aligned)
650 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
653 layer->material_flags = 0;
654 if (backface_culling)
655 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
656 if (tiledef.animation.type != TAT_NONE)
657 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
658 if (tiledef.tileable_horizontal)
659 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
660 if (tiledef.tileable_vertical)
661 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
664 layer->has_color = tiledef.has_color;
665 if (tiledef.has_color)
666 layer->color = tiledef.color;
668 layer->color = color;
670 // Animation parameters
672 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
674 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
675 &frame_count, &frame_length_ms, NULL);
676 layer->animation_frame_count = frame_count;
677 layer->animation_frame_length_ms = frame_length_ms;
680 if (frame_count == 1) {
681 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
683 std::ostringstream os(std::ios::binary);
684 if (!layer->frames) {
685 layer->frames = new std::vector<FrameSpec>();
687 layer->frames->resize(frame_count);
689 for (int i = 0; i < frame_count; i++) {
695 tiledef.animation.getTextureModifer(os,
696 layer->texture->getOriginalSize(), i);
698 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
699 if (layer->normal_texture)
700 frame.normal_texture = tsrc->getNormalTexture(os.str());
701 frame.flags_texture = layer->flags_texture;
702 (*layer->frames)[i] = frame;
707 bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length)
709 video::IVideoDriver *driver = RenderingEngine::get_video_driver();
710 static thread_local bool long_warning_printed = false;
711 std::set<std::string> seen;
713 for (int i = 0; i < length; i++) {
714 if (seen.find(tiles[i].name) != seen.end())
716 seen.insert(tiles[i].name);
718 // Load the texture and see if there's any transparent pixels
719 video::ITexture *texture = tsrc->getTexture(tiles[i].name);
720 video::IImage *image = driver->createImage(texture,
721 core::position2d<s32>(0, 0), texture->getOriginalSize());
724 core::dimension2d<u32> dim = image->getDimension();
726 for (u16 x = 0; x < dim.Width; x++) {
727 for (u16 y = 0; y < dim.Height; y++) {
728 if (image->getPixel(x, y).getAlpha() < 255) {
739 warningstream << "Texture \"" << tiles[i].name << "\" of "
740 << name << " has transparency, assuming "
741 "use_texture_alpha = \"clip\"." << std::endl;
742 if (!long_warning_printed) {
743 warningstream << " This warning can be a false-positive if "
744 "unused pixels in the texture are transparent. However if "
745 "it is meant to be transparent, you *MUST* update the "
746 "nodedef and set use_texture_alpha = \"clip\"! This "
747 "compatibility code will be removed in a few releases."
749 long_warning_printed = true;
756 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
758 if (style == ALIGN_STYLE_WORLD)
760 if (mode == WORLDALIGN_DISABLE)
762 if (style == ALIGN_STYLE_USER_DEFINED)
764 if (drawtype == NDT_NORMAL)
765 return mode >= WORLDALIGN_FORCE;
766 if (drawtype == NDT_NODEBOX)
767 return mode >= WORLDALIGN_FORCE_NODEBOX;
771 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
772 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
774 // minimap pixel color - the average color of a texture
775 if (tsettings.enable_minimap && !tiledef[0].name.empty())
776 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
778 // Figure out the actual tiles to use
780 for (u32 j = 0; j < 6; j++) {
781 tdef[j] = tiledef[j];
782 if (tdef[j].name.empty())
783 tdef[j].name = "unknown_node.png";
785 // also the overlay tiles
786 TileDef tdef_overlay[6];
787 for (u32 j = 0; j < 6; j++)
788 tdef_overlay[j] = tiledef_overlay[j];
789 // also the special tiles
790 TileDef tdef_spec[6];
791 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
792 tdef_spec[j] = tiledef_special[j];
794 bool is_liquid = false;
796 if (alpha == ALPHAMODE_LEGACY_COMPAT) {
797 // Before working with the alpha mode, resolve any legacy kludges
798 alpha = textureAlphaCheck(tsrc, tdef, 6) ? ALPHAMODE_CLIP : ALPHAMODE_OPAQUE;
801 MaterialType material_type = alpha == ALPHAMODE_OPAQUE ?
802 TILE_MATERIAL_OPAQUE : (alpha == ALPHAMODE_CLIP ? TILE_MATERIAL_BASIC :
803 TILE_MATERIAL_ALPHA);
814 if (tsettings.opaque_water)
815 alpha = ALPHAMODE_OPAQUE;
819 case NDT_FLOWINGLIQUID:
821 if (tsettings.opaque_water)
822 alpha = ALPHAMODE_OPAQUE;
827 visual_solidness = 1;
829 case NDT_GLASSLIKE_FRAMED:
831 visual_solidness = 1;
833 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
835 visual_solidness = 1;
836 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
840 visual_solidness = 1;
842 case NDT_ALLFACES_OPTIONAL:
843 if (tsettings.leaves_style == LEAVES_FANCY) {
844 drawtype = NDT_ALLFACES;
846 visual_solidness = 1;
847 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
848 for (u32 j = 0; j < 6; j++) {
849 if (!tdef_spec[j].name.empty())
850 tdef[j].name = tdef_spec[j].name;
852 drawtype = NDT_GLASSLIKE;
854 visual_solidness = 1;
856 drawtype = NDT_NORMAL;
858 for (TileDef &td : tdef)
859 td.name += std::string("^[noalpha");
862 material_type = TILE_MATERIAL_WAVING_LEAVES;
867 material_type = TILE_MATERIAL_WAVING_PLANTS;
876 material_type = TILE_MATERIAL_WAVING_PLANTS;
877 } else if (waving == 2) {
878 material_type = TILE_MATERIAL_WAVING_LEAVES;
879 } else if (waving == 3) {
880 material_type = alpha == ALPHAMODE_OPAQUE ?
881 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
882 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
891 case NDT_PLANTLIKE_ROOTED:
898 material_type = alpha == ALPHAMODE_OPAQUE ?
899 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
900 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
902 material_type = alpha == ALPHAMODE_OPAQUE ? TILE_MATERIAL_LIQUID_OPAQUE :
903 TILE_MATERIAL_LIQUID_TRANSPARENT;
907 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
909 MaterialType overlay_material = material_type;
910 if (overlay_material == TILE_MATERIAL_OPAQUE)
911 overlay_material = TILE_MATERIAL_BASIC;
912 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
913 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
915 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
917 // Tiles (fill in f->tiles[])
918 for (u16 j = 0; j < 6; j++) {
919 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
920 tsettings.world_aligned_mode, drawtype);
921 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
922 color, material_type, tile_shader,
923 tdef[j].backface_culling, tsettings);
924 if (!tdef_overlay[j].name.empty())
925 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
926 color, overlay_material, overlay_shader,
927 tdef[j].backface_culling, tsettings);
930 MaterialType special_material = material_type;
931 if (drawtype == NDT_PLANTLIKE_ROOTED) {
933 special_material = TILE_MATERIAL_WAVING_PLANTS;
934 else if (waving == 2)
935 special_material = TILE_MATERIAL_WAVING_LEAVES;
937 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
939 // Special tiles (fill in f->special_tiles[])
940 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
941 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
942 color, special_material, special_shader,
943 tdef_spec[j].backface_culling, tsettings);
945 if (param_type_2 == CPT2_COLOR ||
946 param_type_2 == CPT2_COLORED_FACEDIR ||
947 param_type_2 == CPT2_COLORED_WALLMOUNTED ||
948 param_type_2 == CPT2_COLORED_DEGROTATE)
949 palette = tsrc->getPalette(palette_name);
951 if (drawtype == NDT_MESH && !mesh.empty()) {
953 // Read the mesh and apply scale
954 mesh_ptr[0] = client->getMesh(mesh);
956 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
957 scaleMesh(mesh_ptr[0], scale);
958 recalculateBoundingBox(mesh_ptr[0]);
959 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
963 //Cache 6dfacedir and wallmounted rotated clones of meshes
964 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
965 (param_type_2 == CPT2_FACEDIR
966 || param_type_2 == CPT2_COLORED_FACEDIR)) {
967 for (u16 j = 1; j < 24; j++) {
968 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
969 rotateMeshBy6dFacedir(mesh_ptr[j], j);
970 recalculateBoundingBox(mesh_ptr[j]);
971 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
973 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
974 && (param_type_2 == CPT2_WALLMOUNTED ||
975 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
976 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
977 for (u16 j = 1; j < 6; j++) {
978 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
979 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
980 recalculateBoundingBox(mesh_ptr[j]);
981 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
983 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
984 recalculateBoundingBox(mesh_ptr[0]);
985 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
997 NodeDefManager::NodeDefManager()
1003 NodeDefManager::~NodeDefManager()
1006 for (ContentFeatures &f : m_content_features) {
1007 for (auto &j : f.mesh_ptr) {
1016 void NodeDefManager::clear()
1018 m_content_features.clear();
1019 m_name_id_mapping.clear();
1020 m_name_id_mapping_with_aliases.clear();
1021 m_group_to_items.clear();
1023 m_selection_box_union.reset(0,0,0);
1024 m_selection_box_int_union.reset(0,0,0);
1026 resetNodeResolveState();
1028 u32 initial_length = 0;
1029 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1030 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1031 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1032 m_content_features.resize(initial_length);
1034 // Set CONTENT_UNKNOWN
1038 // Insert directly into containers
1039 content_t c = CONTENT_UNKNOWN;
1040 m_content_features[c] = f;
1041 addNameIdMapping(c, f.name);
1048 f.drawtype = NDT_AIRLIKE;
1049 f.param_type = CPT_LIGHT;
1050 f.light_propagates = true;
1051 f.sunlight_propagates = true;
1053 f.pointable = false;
1055 f.buildable_to = true;
1057 f.is_ground_content = true;
1058 // Insert directly into containers
1059 content_t c = CONTENT_AIR;
1060 m_content_features[c] = f;
1061 addNameIdMapping(c, f.name);
1064 // Set CONTENT_IGNORE
1068 f.drawtype = NDT_AIRLIKE;
1069 f.param_type = CPT_NONE;
1070 f.light_propagates = false;
1071 f.sunlight_propagates = false;
1073 f.pointable = false;
1075 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1076 f.is_ground_content = true;
1077 // Insert directly into containers
1078 content_t c = CONTENT_IGNORE;
1079 m_content_features[c] = f;
1080 addNameIdMapping(c, f.name);
1085 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1087 std::unordered_map<std::string, content_t>::const_iterator
1088 i = m_name_id_mapping_with_aliases.find(name);
1089 if(i == m_name_id_mapping_with_aliases.end())
1096 content_t NodeDefManager::getId(const std::string &name) const
1098 content_t id = CONTENT_IGNORE;
1104 bool NodeDefManager::getIds(const std::string &name,
1105 std::vector<content_t> &result) const
1107 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1108 if (name.substr(0,6) != "group:") {
1109 content_t id = CONTENT_IGNORE;
1110 bool exists = getId(name, id);
1112 result.push_back(id);
1115 std::string group = name.substr(6);
1117 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1118 i = m_group_to_items.find(group);
1119 if (i == m_group_to_items.end())
1122 const std::vector<content_t> &items = i->second;
1123 result.insert(result.end(), items.begin(), items.end());
1124 //printf("getIds: %dus\n", t.stop());
1129 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1131 content_t id = CONTENT_UNKNOWN;
1137 // returns CONTENT_IGNORE if no free ID found
1138 content_t NodeDefManager::allocateId()
1140 for (content_t id = m_next_id;
1141 id >= m_next_id; // overflow?
1143 while (id >= m_content_features.size()) {
1144 m_content_features.emplace_back();
1146 const ContentFeatures &f = m_content_features[id];
1147 if (f.name.empty()) {
1152 // If we arrive here, an overflow occurred in id.
1153 // That means no ID was found
1154 return CONTENT_IGNORE;
1159 * Returns the smallest box that contains all boxes
1160 * in the vector. Box_union is expanded.
1161 * @param[in] boxes the vector containing the boxes
1162 * @param[in, out] box_union the union of the arguments
1164 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1166 for (const aabb3f &box : boxes) {
1167 box_union->addInternalBox(box);
1173 * Returns a box that contains the nodebox in every case.
1174 * The argument node_union is expanded.
1175 * @param[in] nodebox the nodebox to be measured
1176 * @param[in] features used to decide whether the nodebox
1178 * @param[in, out] box_union the union of the arguments
1180 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1183 switch(nodebox.type) {
1185 case NODEBOX_LEVELED: {
1187 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1188 boxVectorUnion(nodebox.fixed, &half_processed);
1189 // Set leveled boxes to maximal
1190 if (nodebox.type == NODEBOX_LEVELED) {
1191 half_processed.MaxEdge.Y = +BS / 2;
1193 if (features.param_type_2 == CPT2_FACEDIR ||
1194 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1195 // Get maximal coordinate
1197 fabsf(half_processed.MinEdge.X),
1198 fabsf(half_processed.MinEdge.Y),
1199 fabsf(half_processed.MinEdge.Z),
1200 fabsf(half_processed.MaxEdge.X),
1201 fabsf(half_processed.MaxEdge.Y),
1202 fabsf(half_processed.MaxEdge.Z) };
1204 for (float coord : coords) {
1209 // Add the union of all possible rotated boxes
1210 box_union->addInternalPoint(-max, -max, -max);
1211 box_union->addInternalPoint(+max, +max, +max);
1213 box_union->addInternalBox(half_processed);
1217 case NODEBOX_WALLMOUNTED: {
1219 box_union->addInternalBox(nodebox.wall_top);
1220 box_union->addInternalBox(nodebox.wall_bottom);
1221 // Find maximal coordinate in the X-Z plane
1223 fabsf(nodebox.wall_side.MinEdge.X),
1224 fabsf(nodebox.wall_side.MinEdge.Z),
1225 fabsf(nodebox.wall_side.MaxEdge.X),
1226 fabsf(nodebox.wall_side.MaxEdge.Z) };
1228 for (float coord : coords) {
1233 // Add the union of all possible rotated boxes
1234 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1235 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1238 case NODEBOX_CONNECTED: {
1239 // Add all possible connected boxes
1240 boxVectorUnion(nodebox.fixed, box_union);
1241 boxVectorUnion(nodebox.connect_top, box_union);
1242 boxVectorUnion(nodebox.connect_bottom, box_union);
1243 boxVectorUnion(nodebox.connect_front, box_union);
1244 boxVectorUnion(nodebox.connect_left, box_union);
1245 boxVectorUnion(nodebox.connect_back, box_union);
1246 boxVectorUnion(nodebox.connect_right, box_union);
1247 boxVectorUnion(nodebox.disconnected_top, box_union);
1248 boxVectorUnion(nodebox.disconnected_bottom, box_union);
1249 boxVectorUnion(nodebox.disconnected_front, box_union);
1250 boxVectorUnion(nodebox.disconnected_left, box_union);
1251 boxVectorUnion(nodebox.disconnected_back, box_union);
1252 boxVectorUnion(nodebox.disconnected_right, box_union);
1253 boxVectorUnion(nodebox.disconnected, box_union);
1254 boxVectorUnion(nodebox.disconnected_sides, box_union);
1259 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1260 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1266 inline void NodeDefManager::fixSelectionBoxIntUnion()
1268 m_selection_box_int_union.MinEdge.X = floorf(
1269 m_selection_box_union.MinEdge.X / BS + 0.5f);
1270 m_selection_box_int_union.MinEdge.Y = floorf(
1271 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1272 m_selection_box_int_union.MinEdge.Z = floorf(
1273 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1274 m_selection_box_int_union.MaxEdge.X = ceilf(
1275 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1276 m_selection_box_int_union.MaxEdge.Y = ceilf(
1277 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1278 m_selection_box_int_union.MaxEdge.Z = ceilf(
1279 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1283 void NodeDefManager::eraseIdFromGroups(content_t id)
1285 // For all groups in m_group_to_items...
1286 for (auto iter_groups = m_group_to_items.begin();
1287 iter_groups != m_group_to_items.end();) {
1288 // Get the group items vector.
1289 std::vector<content_t> &items = iter_groups->second;
1291 // Remove any occurence of the id in the group items vector.
1292 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1294 // If group is empty, erase its vector from the map.
1296 iter_groups = m_group_to_items.erase(iter_groups);
1303 // IWritableNodeDefManager
1304 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1308 assert(name != "ignore");
1309 assert(name == def.name);
1311 content_t id = CONTENT_IGNORE;
1312 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1315 if (id == CONTENT_IGNORE) {
1316 warningstream << "NodeDefManager: Absolute "
1317 "limit reached" << std::endl;
1318 return CONTENT_IGNORE;
1320 assert(id != CONTENT_IGNORE);
1321 addNameIdMapping(id, name);
1324 // If there is already ContentFeatures registered for this id, clear old groups
1325 if (id < m_content_features.size())
1326 eraseIdFromGroups(id);
1328 m_content_features[id] = def;
1329 verbosestream << "NodeDefManager: registering content id \"" << id
1330 << "\": name=\"" << def.name << "\""<<std::endl;
1332 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1333 fixSelectionBoxIntUnion();
1335 // Add this content to the list of all groups it belongs to
1336 for (const auto &group : def.groups) {
1337 const std::string &group_name = group.first;
1338 m_group_to_items[group_name].push_back(id);
1345 content_t NodeDefManager::allocateDummy(const std::string &name)
1347 assert(name != ""); // Pre-condition
1350 return set(name, f);
1354 void NodeDefManager::removeNode(const std::string &name)
1359 // Erase name from name ID mapping
1360 content_t id = CONTENT_IGNORE;
1361 if (m_name_id_mapping.getId(name, id)) {
1362 m_name_id_mapping.eraseName(name);
1363 m_name_id_mapping_with_aliases.erase(name);
1366 eraseIdFromGroups(id);
1370 void NodeDefManager::updateAliases(IItemDefManager *idef)
1372 std::set<std::string> all;
1374 m_name_id_mapping_with_aliases.clear();
1375 for (const std::string &name : all) {
1376 const std::string &convert_to = idef->getAlias(name);
1378 if (m_name_id_mapping.getId(convert_to, id)) {
1379 m_name_id_mapping_with_aliases.insert(
1380 std::make_pair(name, id));
1385 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1387 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1388 "overrides to textures" << std::endl;
1390 for (const TextureOverride& texture_override : overrides) {
1392 if (!getId(texture_override.id, id))
1393 continue; // Ignore unknown node
1395 ContentFeatures &nodedef = m_content_features[id];
1398 if (texture_override.hasTarget(OverrideTarget::TOP))
1399 nodedef.tiledef[0].name = texture_override.texture;
1401 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1402 nodedef.tiledef[1].name = texture_override.texture;
1404 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1405 nodedef.tiledef[2].name = texture_override.texture;
1407 if (texture_override.hasTarget(OverrideTarget::LEFT))
1408 nodedef.tiledef[3].name = texture_override.texture;
1410 if (texture_override.hasTarget(OverrideTarget::BACK))
1411 nodedef.tiledef[4].name = texture_override.texture;
1413 if (texture_override.hasTarget(OverrideTarget::FRONT))
1414 nodedef.tiledef[5].name = texture_override.texture;
1417 // Override special tiles, if applicable
1418 if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1419 nodedef.tiledef_special[0].name = texture_override.texture;
1421 if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1422 nodedef.tiledef_special[1].name = texture_override.texture;
1424 if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1425 nodedef.tiledef_special[2].name = texture_override.texture;
1427 if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1428 nodedef.tiledef_special[3].name = texture_override.texture;
1430 if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1431 nodedef.tiledef_special[4].name = texture_override.texture;
1433 if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1434 nodedef.tiledef_special[5].name = texture_override.texture;
1438 void NodeDefManager::updateTextures(IGameDef *gamedef,
1439 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1440 void *progress_callback_args)
1443 infostream << "NodeDefManager::updateTextures(): Updating "
1444 "textures in node definitions" << std::endl;
1446 Client *client = (Client *)gamedef;
1447 ITextureSource *tsrc = client->tsrc();
1448 IShaderSource *shdsrc = client->getShaderSource();
1449 auto smgr = client->getSceneManager();
1450 scene::IMeshManipulator *meshmanip = smgr->getMeshManipulator();
1451 TextureSettings tsettings;
1452 tsettings.readSettings();
1454 u32 size = m_content_features.size();
1456 for (u32 i = 0; i < size; i++) {
1457 ContentFeatures *f = &(m_content_features[i]);
1458 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1459 progress_callback(progress_callback_args, i, size);
1464 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1466 writeU8(os, 1); // version
1468 std::ostringstream os2(std::ios::binary);
1469 for (u32 i = 0; i < m_content_features.size(); i++) {
1470 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1471 || i == CONTENT_UNKNOWN)
1473 const ContentFeatures *f = &m_content_features[i];
1474 if (f->name.empty())
1477 // Wrap it in a string to allow different lengths without
1478 // strict version incompatibilities
1479 std::ostringstream wrapper_os(std::ios::binary);
1480 f->serialize(wrapper_os, protocol_version);
1481 os2<<serializeString16(wrapper_os.str());
1483 // must not overflow
1484 u16 next = count + 1;
1485 FATAL_ERROR_IF(next < count, "Overflow");
1488 writeU16(os, count);
1489 os << serializeString32(os2.str());
1493 void NodeDefManager::deSerialize(std::istream &is)
1496 int version = readU8(is);
1498 throw SerializationError("unsupported NodeDefinitionManager version");
1499 u16 count = readU16(is);
1500 std::istringstream is2(deSerializeString32(is), std::ios::binary);
1502 for (u16 n = 0; n < count; n++) {
1503 u16 i = readU16(is2);
1505 // Read it from the string wrapper
1506 std::string wrapper = deSerializeString16(is2);
1507 std::istringstream wrapper_is(wrapper, std::ios::binary);
1508 f.deSerialize(wrapper_is);
1510 // Check error conditions
1511 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1512 warningstream << "NodeDefManager::deSerialize(): "
1513 "not changing builtin node " << i << std::endl;
1516 if (f.name.empty()) {
1517 warningstream << "NodeDefManager::deSerialize(): "
1518 "received empty name" << std::endl;
1524 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1525 warningstream << "NodeDefManager::deSerialize(): "
1526 "already defined with different ID: " << f.name << std::endl;
1530 // All is ok, add node definition with the requested ID
1531 if (i >= m_content_features.size())
1532 m_content_features.resize((u32)(i) + 1);
1533 m_content_features[i] = f;
1534 addNameIdMapping(i, f.name);
1535 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1537 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1538 fixSelectionBoxIntUnion();
1541 // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1542 // are not sent, resolve them client-side too.
1547 void NodeDefManager::addNameIdMapping(content_t i, const std::string &name)
1549 m_name_id_mapping.set(i, name);
1550 m_name_id_mapping_with_aliases.emplace(name, i);
1554 NodeDefManager *createNodeDefManager()
1556 return new NodeDefManager();
1560 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1563 if (m_node_registration_complete)
1564 nr->nodeResolveInternal();
1566 m_pending_resolve_callbacks.push_back(nr);
1570 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1572 size_t len = m_pending_resolve_callbacks.size();
1573 for (size_t i = 0; i != len; i++) {
1574 if (nr != m_pending_resolve_callbacks[i])
1578 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1579 m_pending_resolve_callbacks.resize(len);
1587 void NodeDefManager::runNodeResolveCallbacks()
1589 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1590 NodeResolver *nr = m_pending_resolve_callbacks[i];
1591 nr->nodeResolveInternal();
1594 m_pending_resolve_callbacks.clear();
1598 void NodeDefManager::resetNodeResolveState()
1600 m_node_registration_complete = false;
1601 m_pending_resolve_callbacks.clear();
1604 static void removeDupes(std::vector<content_t> &list)
1606 std::sort(list.begin(), list.end());
1607 auto new_end = std::unique(list.begin(), list.end());
1608 list.erase(new_end, list.end());
1611 void NodeDefManager::resolveCrossrefs()
1613 for (ContentFeatures &f : m_content_features) {
1614 if (f.liquid_type != LIQUID_NONE || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
1615 f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1616 f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1619 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1622 for (const std::string &name : f.connects_to) {
1623 getIds(name, f.connects_to_ids);
1625 removeDupes(f.connects_to_ids);
1629 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1630 u8 connect_face) const
1632 const ContentFeatures &f1 = get(from);
1634 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1637 // lookup target in connected set
1638 if (!CONTAINS(f1.connects_to_ids, to.param0))
1641 const ContentFeatures &f2 = get(to);
1643 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1644 // ignores actually looking if back connection exists
1645 return CONTAINS(f2.connects_to_ids, from.param0);
1647 // does to node declare usable faces?
1648 if (f2.connect_sides > 0) {
1649 if ((f2.param_type_2 == CPT2_FACEDIR ||
1650 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1651 && (connect_face >= 4)) {
1652 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1653 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1655 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1657 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1658 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1660 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1661 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1662 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1664 return (f2.connect_sides
1665 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1667 return (f2.connect_sides & connect_face);
1669 // the target is just a regular node, so connect no matter back connection
1677 NodeResolver::NodeResolver()
1683 NodeResolver::~NodeResolver()
1685 if (!m_resolve_done && m_ndef)
1686 m_ndef->cancelNodeResolveCallback(this);
1690 void NodeResolver::cloneTo(NodeResolver *res) const
1692 FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1693 " after resolving has completed");
1694 /* We don't actually do anything significant. Since the node resolving has
1695 * already completed, the class that called us will already have the
1696 * resolved IDs in its data structures (which it copies on its own) */
1697 res->m_ndef = m_ndef;
1698 res->m_resolve_done = true;
1702 void NodeResolver::nodeResolveInternal()
1704 m_nodenames_idx = 0;
1705 m_nnlistsizes_idx = 0;
1708 m_resolve_done = true;
1710 m_nodenames.clear();
1711 m_nnlistsizes.clear();
1715 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1716 const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1718 if (m_nodenames_idx == m_nodenames.size()) {
1719 *result_out = c_fallback;
1720 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1725 std::string name = m_nodenames[m_nodenames_idx++];
1727 bool success = m_ndef->getId(name, c);
1728 if (!success && !node_alt.empty()) {
1730 success = m_ndef->getId(name, c);
1734 if (error_on_fallback)
1735 errorstream << "NodeResolver: failed to resolve node name '" << name
1736 << "'." << std::endl;
1745 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1746 bool all_required, content_t c_fallback)
1748 bool success = true;
1750 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1751 errorstream << "NodeResolver: no more node lists" << std::endl;
1755 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1758 if (m_nodenames_idx == m_nodenames.size()) {
1759 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1764 std::string &name = m_nodenames[m_nodenames_idx++];
1766 if (name.substr(0,6) != "group:") {
1767 if (m_ndef->getId(name, c)) {
1768 result_out->push_back(c);
1769 } else if (all_required) {
1770 errorstream << "NodeResolver: failed to resolve node name '"
1771 << name << "'." << std::endl;
1772 result_out->push_back(c_fallback);
1776 m_ndef->getIds(name, *result_out);
1783 void NodeResolver::reset(bool resolve_done)
1785 m_nodenames.clear();
1786 m_nodenames_idx = 0;
1787 m_nnlistsizes.clear();
1788 m_nnlistsizes_idx = 0;
1790 m_resolve_done = resolve_done;
1792 m_nodenames.reserve(16);
1793 m_nnlistsizes.reserve(4);