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 palette = tsrc->getPalette(palette_name);
950 if (drawtype == NDT_MESH && !mesh.empty()) {
952 // Read the mesh and apply scale
953 mesh_ptr[0] = client->getMesh(mesh);
955 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
956 scaleMesh(mesh_ptr[0], scale);
957 recalculateBoundingBox(mesh_ptr[0]);
958 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
962 //Cache 6dfacedir and wallmounted rotated clones of meshes
963 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
964 (param_type_2 == CPT2_FACEDIR
965 || param_type_2 == CPT2_COLORED_FACEDIR)) {
966 for (u16 j = 1; j < 24; j++) {
967 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
968 rotateMeshBy6dFacedir(mesh_ptr[j], j);
969 recalculateBoundingBox(mesh_ptr[j]);
970 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
972 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
973 && (param_type_2 == CPT2_WALLMOUNTED ||
974 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
975 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
976 for (u16 j = 1; j < 6; j++) {
977 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
978 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
979 recalculateBoundingBox(mesh_ptr[j]);
980 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
982 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
983 recalculateBoundingBox(mesh_ptr[0]);
984 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
996 NodeDefManager::NodeDefManager()
1002 NodeDefManager::~NodeDefManager()
1005 for (ContentFeatures &f : m_content_features) {
1006 for (auto &j : f.mesh_ptr) {
1015 void NodeDefManager::clear()
1017 m_content_features.clear();
1018 m_name_id_mapping.clear();
1019 m_name_id_mapping_with_aliases.clear();
1020 m_group_to_items.clear();
1022 m_selection_box_union.reset(0,0,0);
1023 m_selection_box_int_union.reset(0,0,0);
1025 resetNodeResolveState();
1027 u32 initial_length = 0;
1028 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1029 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1030 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1031 m_content_features.resize(initial_length);
1033 // Set CONTENT_UNKNOWN
1037 // Insert directly into containers
1038 content_t c = CONTENT_UNKNOWN;
1039 m_content_features[c] = f;
1040 addNameIdMapping(c, f.name);
1047 f.drawtype = NDT_AIRLIKE;
1048 f.param_type = CPT_LIGHT;
1049 f.light_propagates = true;
1050 f.sunlight_propagates = true;
1052 f.pointable = false;
1054 f.buildable_to = true;
1056 f.is_ground_content = true;
1057 // Insert directly into containers
1058 content_t c = CONTENT_AIR;
1059 m_content_features[c] = f;
1060 addNameIdMapping(c, f.name);
1063 // Set CONTENT_IGNORE
1067 f.drawtype = NDT_AIRLIKE;
1068 f.param_type = CPT_NONE;
1069 f.light_propagates = false;
1070 f.sunlight_propagates = false;
1072 f.pointable = false;
1074 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1075 f.is_ground_content = true;
1076 // Insert directly into containers
1077 content_t c = CONTENT_IGNORE;
1078 m_content_features[c] = f;
1079 addNameIdMapping(c, f.name);
1084 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1086 std::unordered_map<std::string, content_t>::const_iterator
1087 i = m_name_id_mapping_with_aliases.find(name);
1088 if(i == m_name_id_mapping_with_aliases.end())
1095 content_t NodeDefManager::getId(const std::string &name) const
1097 content_t id = CONTENT_IGNORE;
1103 bool NodeDefManager::getIds(const std::string &name,
1104 std::vector<content_t> &result) const
1106 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1107 if (name.substr(0,6) != "group:") {
1108 content_t id = CONTENT_IGNORE;
1109 bool exists = getId(name, id);
1111 result.push_back(id);
1114 std::string group = name.substr(6);
1116 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1117 i = m_group_to_items.find(group);
1118 if (i == m_group_to_items.end())
1121 const std::vector<content_t> &items = i->second;
1122 result.insert(result.end(), items.begin(), items.end());
1123 //printf("getIds: %dus\n", t.stop());
1128 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1130 content_t id = CONTENT_UNKNOWN;
1136 // returns CONTENT_IGNORE if no free ID found
1137 content_t NodeDefManager::allocateId()
1139 for (content_t id = m_next_id;
1140 id >= m_next_id; // overflow?
1142 while (id >= m_content_features.size()) {
1143 m_content_features.emplace_back();
1145 const ContentFeatures &f = m_content_features[id];
1146 if (f.name.empty()) {
1151 // If we arrive here, an overflow occurred in id.
1152 // That means no ID was found
1153 return CONTENT_IGNORE;
1158 * Returns the smallest box that contains all boxes
1159 * in the vector. Box_union is expanded.
1160 * @param[in] boxes the vector containing the boxes
1161 * @param[in, out] box_union the union of the arguments
1163 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1165 for (const aabb3f &box : boxes) {
1166 box_union->addInternalBox(box);
1172 * Returns a box that contains the nodebox in every case.
1173 * The argument node_union is expanded.
1174 * @param[in] nodebox the nodebox to be measured
1175 * @param[in] features used to decide whether the nodebox
1177 * @param[in, out] box_union the union of the arguments
1179 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1182 switch(nodebox.type) {
1184 case NODEBOX_LEVELED: {
1186 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1187 boxVectorUnion(nodebox.fixed, &half_processed);
1188 // Set leveled boxes to maximal
1189 if (nodebox.type == NODEBOX_LEVELED) {
1190 half_processed.MaxEdge.Y = +BS / 2;
1192 if (features.param_type_2 == CPT2_FACEDIR ||
1193 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1194 // Get maximal coordinate
1196 fabsf(half_processed.MinEdge.X),
1197 fabsf(half_processed.MinEdge.Y),
1198 fabsf(half_processed.MinEdge.Z),
1199 fabsf(half_processed.MaxEdge.X),
1200 fabsf(half_processed.MaxEdge.Y),
1201 fabsf(half_processed.MaxEdge.Z) };
1203 for (float coord : coords) {
1208 // Add the union of all possible rotated boxes
1209 box_union->addInternalPoint(-max, -max, -max);
1210 box_union->addInternalPoint(+max, +max, +max);
1212 box_union->addInternalBox(half_processed);
1216 case NODEBOX_WALLMOUNTED: {
1218 box_union->addInternalBox(nodebox.wall_top);
1219 box_union->addInternalBox(nodebox.wall_bottom);
1220 // Find maximal coordinate in the X-Z plane
1222 fabsf(nodebox.wall_side.MinEdge.X),
1223 fabsf(nodebox.wall_side.MinEdge.Z),
1224 fabsf(nodebox.wall_side.MaxEdge.X),
1225 fabsf(nodebox.wall_side.MaxEdge.Z) };
1227 for (float coord : coords) {
1232 // Add the union of all possible rotated boxes
1233 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1234 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1237 case NODEBOX_CONNECTED: {
1238 // Add all possible connected boxes
1239 boxVectorUnion(nodebox.fixed, box_union);
1240 boxVectorUnion(nodebox.connect_top, box_union);
1241 boxVectorUnion(nodebox.connect_bottom, box_union);
1242 boxVectorUnion(nodebox.connect_front, box_union);
1243 boxVectorUnion(nodebox.connect_left, box_union);
1244 boxVectorUnion(nodebox.connect_back, box_union);
1245 boxVectorUnion(nodebox.connect_right, box_union);
1246 boxVectorUnion(nodebox.disconnected_top, box_union);
1247 boxVectorUnion(nodebox.disconnected_bottom, box_union);
1248 boxVectorUnion(nodebox.disconnected_front, box_union);
1249 boxVectorUnion(nodebox.disconnected_left, box_union);
1250 boxVectorUnion(nodebox.disconnected_back, box_union);
1251 boxVectorUnion(nodebox.disconnected_right, box_union);
1252 boxVectorUnion(nodebox.disconnected, box_union);
1253 boxVectorUnion(nodebox.disconnected_sides, box_union);
1258 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1259 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1265 inline void NodeDefManager::fixSelectionBoxIntUnion()
1267 m_selection_box_int_union.MinEdge.X = floorf(
1268 m_selection_box_union.MinEdge.X / BS + 0.5f);
1269 m_selection_box_int_union.MinEdge.Y = floorf(
1270 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1271 m_selection_box_int_union.MinEdge.Z = floorf(
1272 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1273 m_selection_box_int_union.MaxEdge.X = ceilf(
1274 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1275 m_selection_box_int_union.MaxEdge.Y = ceilf(
1276 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1277 m_selection_box_int_union.MaxEdge.Z = ceilf(
1278 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1282 void NodeDefManager::eraseIdFromGroups(content_t id)
1284 // For all groups in m_group_to_items...
1285 for (auto iter_groups = m_group_to_items.begin();
1286 iter_groups != m_group_to_items.end();) {
1287 // Get the group items vector.
1288 std::vector<content_t> &items = iter_groups->second;
1290 // Remove any occurence of the id in the group items vector.
1291 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1293 // If group is empty, erase its vector from the map.
1295 iter_groups = m_group_to_items.erase(iter_groups);
1302 // IWritableNodeDefManager
1303 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1307 assert(name != "ignore");
1308 assert(name == def.name);
1310 content_t id = CONTENT_IGNORE;
1311 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1314 if (id == CONTENT_IGNORE) {
1315 warningstream << "NodeDefManager: Absolute "
1316 "limit reached" << std::endl;
1317 return CONTENT_IGNORE;
1319 assert(id != CONTENT_IGNORE);
1320 addNameIdMapping(id, name);
1323 // If there is already ContentFeatures registered for this id, clear old groups
1324 if (id < m_content_features.size())
1325 eraseIdFromGroups(id);
1327 m_content_features[id] = def;
1328 verbosestream << "NodeDefManager: registering content id \"" << id
1329 << "\": name=\"" << def.name << "\""<<std::endl;
1331 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1332 fixSelectionBoxIntUnion();
1334 // Add this content to the list of all groups it belongs to
1335 for (const auto &group : def.groups) {
1336 const std::string &group_name = group.first;
1337 m_group_to_items[group_name].push_back(id);
1344 content_t NodeDefManager::allocateDummy(const std::string &name)
1346 assert(name != ""); // Pre-condition
1349 return set(name, f);
1353 void NodeDefManager::removeNode(const std::string &name)
1358 // Erase name from name ID mapping
1359 content_t id = CONTENT_IGNORE;
1360 if (m_name_id_mapping.getId(name, id)) {
1361 m_name_id_mapping.eraseName(name);
1362 m_name_id_mapping_with_aliases.erase(name);
1365 eraseIdFromGroups(id);
1369 void NodeDefManager::updateAliases(IItemDefManager *idef)
1371 std::set<std::string> all;
1373 m_name_id_mapping_with_aliases.clear();
1374 for (const std::string &name : all) {
1375 const std::string &convert_to = idef->getAlias(name);
1377 if (m_name_id_mapping.getId(convert_to, id)) {
1378 m_name_id_mapping_with_aliases.insert(
1379 std::make_pair(name, id));
1384 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1386 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1387 "overrides to textures" << std::endl;
1389 for (const TextureOverride& texture_override : overrides) {
1391 if (!getId(texture_override.id, id))
1392 continue; // Ignore unknown node
1394 ContentFeatures &nodedef = m_content_features[id];
1397 if (texture_override.hasTarget(OverrideTarget::TOP))
1398 nodedef.tiledef[0].name = texture_override.texture;
1400 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1401 nodedef.tiledef[1].name = texture_override.texture;
1403 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1404 nodedef.tiledef[2].name = texture_override.texture;
1406 if (texture_override.hasTarget(OverrideTarget::LEFT))
1407 nodedef.tiledef[3].name = texture_override.texture;
1409 if (texture_override.hasTarget(OverrideTarget::BACK))
1410 nodedef.tiledef[4].name = texture_override.texture;
1412 if (texture_override.hasTarget(OverrideTarget::FRONT))
1413 nodedef.tiledef[5].name = texture_override.texture;
1416 // Override special tiles, if applicable
1417 if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1418 nodedef.tiledef_special[0].name = texture_override.texture;
1420 if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1421 nodedef.tiledef_special[1].name = texture_override.texture;
1423 if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1424 nodedef.tiledef_special[2].name = texture_override.texture;
1426 if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1427 nodedef.tiledef_special[3].name = texture_override.texture;
1429 if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1430 nodedef.tiledef_special[4].name = texture_override.texture;
1432 if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1433 nodedef.tiledef_special[5].name = texture_override.texture;
1437 void NodeDefManager::updateTextures(IGameDef *gamedef,
1438 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1439 void *progress_callback_args)
1442 infostream << "NodeDefManager::updateTextures(): Updating "
1443 "textures in node definitions" << std::endl;
1445 Client *client = (Client *)gamedef;
1446 ITextureSource *tsrc = client->tsrc();
1447 IShaderSource *shdsrc = client->getShaderSource();
1448 scene::IMeshManipulator *meshmanip =
1449 RenderingEngine::get_scene_manager()->getMeshManipulator();
1450 TextureSettings tsettings;
1451 tsettings.readSettings();
1453 u32 size = m_content_features.size();
1455 for (u32 i = 0; i < size; i++) {
1456 ContentFeatures *f = &(m_content_features[i]);
1457 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1458 progress_callback(progress_callback_args, i, size);
1463 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1465 writeU8(os, 1); // version
1467 std::ostringstream os2(std::ios::binary);
1468 for (u32 i = 0; i < m_content_features.size(); i++) {
1469 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1470 || i == CONTENT_UNKNOWN)
1472 const ContentFeatures *f = &m_content_features[i];
1473 if (f->name.empty())
1476 // Wrap it in a string to allow different lengths without
1477 // strict version incompatibilities
1478 std::ostringstream wrapper_os(std::ios::binary);
1479 f->serialize(wrapper_os, protocol_version);
1480 os2<<serializeString16(wrapper_os.str());
1482 // must not overflow
1483 u16 next = count + 1;
1484 FATAL_ERROR_IF(next < count, "Overflow");
1487 writeU16(os, count);
1488 os << serializeString32(os2.str());
1492 void NodeDefManager::deSerialize(std::istream &is)
1495 int version = readU8(is);
1497 throw SerializationError("unsupported NodeDefinitionManager version");
1498 u16 count = readU16(is);
1499 std::istringstream is2(deSerializeString32(is), std::ios::binary);
1501 for (u16 n = 0; n < count; n++) {
1502 u16 i = readU16(is2);
1504 // Read it from the string wrapper
1505 std::string wrapper = deSerializeString16(is2);
1506 std::istringstream wrapper_is(wrapper, std::ios::binary);
1507 f.deSerialize(wrapper_is);
1509 // Check error conditions
1510 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1511 warningstream << "NodeDefManager::deSerialize(): "
1512 "not changing builtin node " << i << std::endl;
1515 if (f.name.empty()) {
1516 warningstream << "NodeDefManager::deSerialize(): "
1517 "received empty name" << std::endl;
1523 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1524 warningstream << "NodeDefManager::deSerialize(): "
1525 "already defined with different ID: " << f.name << std::endl;
1529 // All is ok, add node definition with the requested ID
1530 if (i >= m_content_features.size())
1531 m_content_features.resize((u32)(i) + 1);
1532 m_content_features[i] = f;
1533 addNameIdMapping(i, f.name);
1534 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1536 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1537 fixSelectionBoxIntUnion();
1540 // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1541 // are not sent, resolve them client-side too.
1546 void NodeDefManager::addNameIdMapping(content_t i, std::string name)
1548 m_name_id_mapping.set(i, name);
1549 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1553 NodeDefManager *createNodeDefManager()
1555 return new NodeDefManager();
1559 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1562 if (m_node_registration_complete)
1563 nr->nodeResolveInternal();
1565 m_pending_resolve_callbacks.push_back(nr);
1569 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1571 size_t len = m_pending_resolve_callbacks.size();
1572 for (size_t i = 0; i != len; i++) {
1573 if (nr != m_pending_resolve_callbacks[i])
1577 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1578 m_pending_resolve_callbacks.resize(len);
1586 void NodeDefManager::runNodeResolveCallbacks()
1588 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1589 NodeResolver *nr = m_pending_resolve_callbacks[i];
1590 nr->nodeResolveInternal();
1593 m_pending_resolve_callbacks.clear();
1597 void NodeDefManager::resetNodeResolveState()
1599 m_node_registration_complete = false;
1600 m_pending_resolve_callbacks.clear();
1603 static void removeDupes(std::vector<content_t> &list)
1605 std::sort(list.begin(), list.end());
1606 auto new_end = std::unique(list.begin(), list.end());
1607 list.erase(new_end, list.end());
1610 void NodeDefManager::resolveCrossrefs()
1612 for (ContentFeatures &f : m_content_features) {
1613 if (f.liquid_type != LIQUID_NONE || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
1614 f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1615 f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1618 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1621 for (const std::string &name : f.connects_to) {
1622 getIds(name, f.connects_to_ids);
1624 removeDupes(f.connects_to_ids);
1628 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1629 u8 connect_face) const
1631 const ContentFeatures &f1 = get(from);
1633 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1636 // lookup target in connected set
1637 if (!CONTAINS(f1.connects_to_ids, to.param0))
1640 const ContentFeatures &f2 = get(to);
1642 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1643 // ignores actually looking if back connection exists
1644 return CONTAINS(f2.connects_to_ids, from.param0);
1646 // does to node declare usable faces?
1647 if (f2.connect_sides > 0) {
1648 if ((f2.param_type_2 == CPT2_FACEDIR ||
1649 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1650 && (connect_face >= 4)) {
1651 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1652 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1654 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1656 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1657 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1659 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 32, 16, 8, 4 // 32 - left
1663 return (f2.connect_sides
1664 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1666 return (f2.connect_sides & connect_face);
1668 // the target is just a regular node, so connect no matter back connection
1676 NodeResolver::NodeResolver()
1678 m_nodenames.reserve(16);
1679 m_nnlistsizes.reserve(4);
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);