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 &d)
1306 ContentFeatures def = d;
1310 assert(name != "ignore");
1311 assert(name == def.name);
1313 content_t id = CONTENT_IGNORE;
1315 if (m_name_id_mapping.getId(name, id)) {
1317 ContentFeatures old_def = get(name);
1318 for (u32 j = 0; j < 6; j++)
1319 if (def.tiledef[j].name.empty())
1320 def.tiledef[j] = old_def.tiledef[j];
1321 for (u32 j = 0; j < 6; j++)
1322 if (def.tiledef_overlay[j].name.empty())
1323 def.tiledef_overlay[j] = old_def.tiledef_overlay[j];
1324 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
1325 if (def.tiledef_special[j].name.empty())
1326 def.tiledef_special[j] = old_def.tiledef_special[j];
1331 if (id == CONTENT_IGNORE) {
1332 warningstream << "NodeDefManager: Absolute "
1333 "limit reached" << std::endl;
1334 return CONTENT_IGNORE;
1336 assert(id != CONTENT_IGNORE);
1337 addNameIdMapping(id, name);
1340 // If there is already ContentFeatures registered for this id, clear old groups
1341 if (id < m_content_features.size())
1342 eraseIdFromGroups(id);
1344 m_content_features[id] = def;
1345 verbosestream << "NodeDefManager: registering content id \"" << id
1346 << "\": name=\"" << def.name << "\""<<std::endl;
1348 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1349 fixSelectionBoxIntUnion();
1351 // Add this content to the list of all groups it belongs to
1352 for (const auto &group : def.groups) {
1353 const std::string &group_name = group.first;
1354 m_group_to_items[group_name].push_back(id);
1361 content_t NodeDefManager::allocateDummy(const std::string &name)
1363 assert(name != ""); // Pre-condition
1366 return set(name, f);
1370 void NodeDefManager::removeNode(const std::string &name)
1375 // Erase name from name ID mapping
1376 content_t id = CONTENT_IGNORE;
1377 if (m_name_id_mapping.getId(name, id)) {
1378 m_name_id_mapping.eraseName(name);
1379 m_name_id_mapping_with_aliases.erase(name);
1382 eraseIdFromGroups(id);
1386 void NodeDefManager::updateAliases(IItemDefManager *idef)
1388 std::set<std::string> all;
1390 m_name_id_mapping_with_aliases.clear();
1391 for (const std::string &name : all) {
1392 const std::string &convert_to = idef->getAlias(name);
1394 if (m_name_id_mapping.getId(convert_to, id)) {
1395 m_name_id_mapping_with_aliases.insert(
1396 std::make_pair(name, id));
1401 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1403 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1404 "overrides to textures" << std::endl;
1406 for (const TextureOverride& texture_override : overrides) {
1408 if (!getId(texture_override.id, id))
1409 continue; // Ignore unknown node
1411 ContentFeatures &nodedef = m_content_features[id];
1414 if (texture_override.hasTarget(OverrideTarget::TOP))
1415 nodedef.tiledef[0].name = texture_override.texture;
1417 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1418 nodedef.tiledef[1].name = texture_override.texture;
1420 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1421 nodedef.tiledef[2].name = texture_override.texture;
1423 if (texture_override.hasTarget(OverrideTarget::LEFT))
1424 nodedef.tiledef[3].name = texture_override.texture;
1426 if (texture_override.hasTarget(OverrideTarget::BACK))
1427 nodedef.tiledef[4].name = texture_override.texture;
1429 if (texture_override.hasTarget(OverrideTarget::FRONT))
1430 nodedef.tiledef[5].name = texture_override.texture;
1433 // Override special tiles, if applicable
1434 if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1435 nodedef.tiledef_special[0].name = texture_override.texture;
1437 if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1438 nodedef.tiledef_special[1].name = texture_override.texture;
1440 if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1441 nodedef.tiledef_special[2].name = texture_override.texture;
1443 if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1444 nodedef.tiledef_special[3].name = texture_override.texture;
1446 if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1447 nodedef.tiledef_special[4].name = texture_override.texture;
1449 if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1450 nodedef.tiledef_special[5].name = texture_override.texture;
1454 void NodeDefManager::updateTextures(IGameDef *gamedef, void *progress_callback_args)
1457 infostream << "NodeDefManager::updateTextures(): Updating "
1458 "textures in node definitions" << std::endl;
1460 Client *client = (Client *)gamedef;
1461 ITextureSource *tsrc = client->tsrc();
1462 IShaderSource *shdsrc = client->getShaderSource();
1463 auto smgr = client->getSceneManager();
1464 scene::IMeshManipulator *meshmanip = smgr->getMeshManipulator();
1465 TextureSettings tsettings;
1466 tsettings.readSettings();
1468 u32 size = m_content_features.size();
1470 for (u32 i = 0; i < size; i++) {
1471 ContentFeatures *f = &(m_content_features[i]);
1472 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1473 client->showUpdateProgressTexture(progress_callback_args, i, size);
1478 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1480 writeU8(os, 1); // version
1482 std::ostringstream os2(std::ios::binary);
1483 for (u32 i = 0; i < m_content_features.size(); i++) {
1484 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1485 || i == CONTENT_UNKNOWN)
1487 const ContentFeatures *f = &m_content_features[i];
1488 if (f->name.empty())
1491 // Wrap it in a string to allow different lengths without
1492 // strict version incompatibilities
1493 std::ostringstream wrapper_os(std::ios::binary);
1494 f->serialize(wrapper_os, protocol_version);
1495 os2<<serializeString16(wrapper_os.str());
1497 // must not overflow
1498 u16 next = count + 1;
1499 FATAL_ERROR_IF(next < count, "Overflow");
1502 writeU16(os, count);
1503 os << serializeString32(os2.str());
1507 void NodeDefManager::deSerialize(std::istream &is)
1510 int version = readU8(is);
1512 throw SerializationError("unsupported NodeDefinitionManager version");
1513 u16 count = readU16(is);
1514 std::istringstream is2(deSerializeString32(is), std::ios::binary);
1516 for (u16 n = 0; n < count; n++) {
1517 u16 i = readU16(is2);
1519 // Read it from the string wrapper
1520 std::string wrapper = deSerializeString16(is2);
1521 std::istringstream wrapper_is(wrapper, std::ios::binary);
1522 f.deSerialize(wrapper_is);
1524 // Check error conditions
1525 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1526 warningstream << "NodeDefManager::deSerialize(): "
1527 "not changing builtin node " << i << std::endl;
1530 if (f.name.empty()) {
1531 warningstream << "NodeDefManager::deSerialize(): "
1532 "received empty name" << std::endl;
1538 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1539 warningstream << "NodeDefManager::deSerialize(): "
1540 "already defined with different ID: " << f.name << std::endl;
1544 // All is ok, add node definition with the requested ID
1545 if (i >= m_content_features.size())
1546 m_content_features.resize((u32)(i) + 1);
1547 m_content_features[i] = f;
1548 addNameIdMapping(i, f.name);
1549 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1551 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1552 fixSelectionBoxIntUnion();
1555 // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1556 // are not sent, resolve them client-side too.
1561 void NodeDefManager::addNameIdMapping(content_t i, const std::string &name)
1563 m_name_id_mapping.set(i, name);
1564 m_name_id_mapping_with_aliases.emplace(name, i);
1568 NodeDefManager *createNodeDefManager()
1570 return new NodeDefManager();
1574 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1577 if (m_node_registration_complete)
1578 nr->nodeResolveInternal();
1580 m_pending_resolve_callbacks.push_back(nr);
1584 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1586 size_t len = m_pending_resolve_callbacks.size();
1587 for (size_t i = 0; i != len; i++) {
1588 if (nr != m_pending_resolve_callbacks[i])
1592 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1593 m_pending_resolve_callbacks.resize(len);
1601 void NodeDefManager::runNodeResolveCallbacks()
1603 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1604 NodeResolver *nr = m_pending_resolve_callbacks[i];
1605 nr->nodeResolveInternal();
1608 m_pending_resolve_callbacks.clear();
1612 void NodeDefManager::resetNodeResolveState()
1614 m_node_registration_complete = false;
1615 m_pending_resolve_callbacks.clear();
1618 static void removeDupes(std::vector<content_t> &list)
1620 std::sort(list.begin(), list.end());
1621 auto new_end = std::unique(list.begin(), list.end());
1622 list.erase(new_end, list.end());
1625 void NodeDefManager::resolveCrossrefs()
1627 for (ContentFeatures &f : m_content_features) {
1628 if (f.liquid_type != LIQUID_NONE || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
1629 f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1630 f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1633 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1636 for (const std::string &name : f.connects_to) {
1637 getIds(name, f.connects_to_ids);
1639 removeDupes(f.connects_to_ids);
1643 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1644 u8 connect_face) const
1646 const ContentFeatures &f1 = get(from);
1648 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1651 // lookup target in connected set
1652 if (!CONTAINS(f1.connects_to_ids, to.param0))
1655 const ContentFeatures &f2 = get(to);
1657 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1658 // ignores actually looking if back connection exists
1659 return CONTAINS(f2.connects_to_ids, from.param0);
1661 // does to node declare usable faces?
1662 if (f2.connect_sides > 0) {
1663 if ((f2.param_type_2 == CPT2_FACEDIR ||
1664 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1665 && (connect_face >= 4)) {
1666 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1667 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1669 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1671 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1672 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1674 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 32, 16, 8, 4 // 32 - left
1678 return (f2.connect_sides
1679 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1681 return (f2.connect_sides & connect_face);
1683 // the target is just a regular node, so connect no matter back connection
1691 NodeResolver::NodeResolver()
1697 NodeResolver::~NodeResolver()
1699 if (!m_resolve_done && m_ndef)
1700 m_ndef->cancelNodeResolveCallback(this);
1704 void NodeResolver::cloneTo(NodeResolver *res) const
1706 FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1707 " after resolving has completed");
1708 /* We don't actually do anything significant. Since the node resolving has
1709 * already completed, the class that called us will already have the
1710 * resolved IDs in its data structures (which it copies on its own) */
1711 res->m_ndef = m_ndef;
1712 res->m_resolve_done = true;
1716 void NodeResolver::nodeResolveInternal()
1718 m_nodenames_idx = 0;
1719 m_nnlistsizes_idx = 0;
1722 m_resolve_done = true;
1724 m_nodenames.clear();
1725 m_nnlistsizes.clear();
1729 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1730 const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1732 if (m_nodenames_idx == m_nodenames.size()) {
1733 *result_out = c_fallback;
1734 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1739 std::string name = m_nodenames[m_nodenames_idx++];
1741 bool success = m_ndef->getId(name, c);
1742 if (!success && !node_alt.empty()) {
1744 success = m_ndef->getId(name, c);
1748 if (error_on_fallback)
1749 errorstream << "NodeResolver: failed to resolve node name '" << name
1750 << "'." << std::endl;
1759 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1760 bool all_required, content_t c_fallback)
1762 bool success = true;
1764 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1765 errorstream << "NodeResolver: no more node lists" << std::endl;
1769 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1772 if (m_nodenames_idx == m_nodenames.size()) {
1773 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1778 std::string &name = m_nodenames[m_nodenames_idx++];
1780 if (name.substr(0,6) != "group:") {
1781 if (m_ndef->getId(name, c)) {
1782 result_out->push_back(c);
1783 } else if (all_required) {
1784 errorstream << "NodeResolver: failed to resolve node name '"
1785 << name << "'." << std::endl;
1786 result_out->push_back(c_fallback);
1790 m_ndef->getIds(name, *result_out);
1797 void NodeResolver::reset(bool resolve_done)
1799 m_nodenames.clear();
1800 m_nodenames_idx = 0;
1801 m_nnlistsizes.clear();
1802 m_nnlistsizes_idx = 0;
1804 m_resolve_done = resolve_done;
1806 m_nodenames.reserve(16);
1807 m_nnlistsizes.reserve(4);