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 enable_shaders = g_settings->getBool("enable_shaders");
270 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
271 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
272 bool smooth_lighting = g_settings->getBool("smooth_lighting");
273 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
274 enable_minimap = g_settings->getBool("enable_minimap");
275 node_texture_size = g_settings->getU16("texture_min_size");
276 std::string leaves_style_str = g_settings->get("leaves_style");
277 std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
278 std::string autoscale_mode_str = g_settings->get("autoscale_mode");
280 // Mesh cache is not supported in combination with smooth lighting
282 enable_mesh_cache = false;
284 use_normal_texture = enable_shaders &&
285 (enable_bumpmapping || enable_parallax_occlusion);
286 if (leaves_style_str == "fancy") {
287 leaves_style = LEAVES_FANCY;
288 } else if (leaves_style_str == "simple") {
289 leaves_style = LEAVES_SIMPLE;
291 leaves_style = LEAVES_OPAQUE;
294 if (world_aligned_mode_str == "enable")
295 world_aligned_mode = WORLDALIGN_ENABLE;
296 else if (world_aligned_mode_str == "force_solid")
297 world_aligned_mode = WORLDALIGN_FORCE;
298 else if (world_aligned_mode_str == "force_nodebox")
299 world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
301 world_aligned_mode = WORLDALIGN_DISABLE;
303 if (autoscale_mode_str == "enable")
304 autoscale_mode = AUTOSCALE_ENABLE;
305 else if (autoscale_mode_str == "force")
306 autoscale_mode = AUTOSCALE_FORCE;
308 autoscale_mode = AUTOSCALE_DISABLE;
315 ContentFeatures::ContentFeatures()
320 ContentFeatures::~ContentFeatures()
323 for (u16 j = 0; j < 6; j++) {
324 delete tiles[j].layers[0].frames;
325 delete tiles[j].layers[1].frames;
327 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
328 delete special_tiles[j].layers[0].frames;
332 void ContentFeatures::reset()
339 visual_solidness = 0;
340 backface_culling = true;
343 has_on_construct = false;
344 has_on_destruct = false;
345 has_after_destruct = false;
349 NOTE: Most of this is always overridden by the default values given
354 // Unknown nodes can be dug
355 groups["dig_immediate"] = 2;
356 drawtype = NDT_NORMAL;
359 for (auto &i : mesh_ptr)
361 minimap_color = video::SColor(0, 0, 0, 0);
364 for (auto &i : tiledef)
366 for (auto &j : tiledef_special)
369 post_effect_color = video::SColor(0, 0, 0, 0);
370 param_type = CPT_NONE;
371 param_type_2 = CPT2_NONE;
372 is_ground_content = false;
373 light_propagates = false;
374 sunlight_propagates = false;
379 buildable_to = false;
381 rightclickable = true;
383 leveled_max = LEVELED_MAX;
384 liquid_type = LIQUID_NONE;
385 liquid_alternative_flowing = "";
386 liquid_alternative_flowing_id = CONTENT_IGNORE;
387 liquid_alternative_source = "";
388 liquid_alternative_source_id = CONTENT_IGNORE;
389 liquid_viscosity = 0;
390 liquid_renewable = true;
391 liquid_range = LIQUID_LEVEL_MAX+1;
394 damage_per_second = 0;
395 node_box = NodeBox();
396 selection_box = NodeBox();
397 collision_box = NodeBox();
399 legacy_facedir_simple = false;
400 legacy_wallmounted = false;
401 sound_footstep = SimpleSoundSpec();
402 sound_dig = SimpleSoundSpec("__group");
403 sound_dug = SimpleSoundSpec();
405 connects_to_ids.clear();
407 color = video::SColor(0xFFFFFFFF);
410 node_dig_prediction = "air";
413 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
415 const u8 version = CONTENTFEATURES_VERSION;
416 writeU8(os, version);
419 os << serializeString16(name);
420 writeU16(os, groups.size());
421 for (const auto &group : groups) {
422 os << serializeString16(group.first);
423 writeS16(os, group.second);
425 writeU8(os, param_type);
426 writeU8(os, param_type_2);
429 writeU8(os, drawtype);
430 os << serializeString16(mesh);
431 writeF32(os, visual_scale);
433 for (const TileDef &td : tiledef)
434 td.serialize(os, protocol_version);
435 for (const TileDef &td : tiledef_overlay)
436 td.serialize(os, protocol_version);
437 writeU8(os, CF_SPECIAL_COUNT);
438 for (const TileDef &td : tiledef_special) {
439 td.serialize(os, protocol_version);
442 writeU8(os, color.getRed());
443 writeU8(os, color.getGreen());
444 writeU8(os, color.getBlue());
445 os << serializeString16(palette_name);
447 writeU8(os, connect_sides);
448 writeU16(os, connects_to_ids.size());
449 for (u16 connects_to_id : connects_to_ids)
450 writeU16(os, connects_to_id);
451 writeARGB8(os, post_effect_color);
452 writeU8(os, leveled);
455 writeU8(os, light_propagates);
456 writeU8(os, sunlight_propagates);
457 writeU8(os, light_source);
460 writeU8(os, is_ground_content);
463 writeU8(os, walkable);
464 writeU8(os, pointable);
465 writeU8(os, diggable);
466 writeU8(os, climbable);
467 writeU8(os, buildable_to);
468 writeU8(os, rightclickable);
469 writeU32(os, damage_per_second);
472 writeU8(os, liquid_type);
473 os << serializeString16(liquid_alternative_flowing);
474 os << serializeString16(liquid_alternative_source);
475 writeU8(os, liquid_viscosity);
476 writeU8(os, liquid_renewable);
477 writeU8(os, liquid_range);
478 writeU8(os, drowning);
479 writeU8(os, floodable);
482 node_box.serialize(os, protocol_version);
483 selection_box.serialize(os, protocol_version);
484 collision_box.serialize(os, protocol_version);
487 sound_footstep.serialize(os, version);
488 sound_dig.serialize(os, version);
489 sound_dug.serialize(os, version);
492 writeU8(os, legacy_facedir_simple);
493 writeU8(os, legacy_wallmounted);
495 os << serializeString16(node_dig_prediction);
496 writeU8(os, leveled_max);
499 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
501 // alpha == 0 means that the node is using texture alpha
502 if (alpha == 0 || alpha == 255)
505 for (int i = 0; i < length; i++) {
506 if (tiles[i].name.empty())
509 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
510 tiles[i].name = s.str();
514 void ContentFeatures::deSerialize(std::istream &is)
517 const u8 version = readU8(is);
518 if (version < CONTENTFEATURES_VERSION)
519 throw SerializationError("unsupported ContentFeatures version");
522 name = deSerializeString16(is);
524 u32 groups_size = readU16(is);
525 for (u32 i = 0; i < groups_size; i++) {
526 std::string name = deSerializeString16(is);
527 int value = readS16(is);
528 groups[name] = value;
530 param_type = (enum ContentParamType) readU8(is);
531 param_type_2 = (enum ContentParamType2) readU8(is);
534 drawtype = (enum NodeDrawType) readU8(is);
535 mesh = deSerializeString16(is);
536 visual_scale = readF32(is);
538 throw SerializationError("unsupported tile count");
539 for (TileDef &td : tiledef)
540 td.deSerialize(is, version, drawtype);
541 for (TileDef &td : tiledef_overlay)
542 td.deSerialize(is, version, drawtype);
543 if (readU8(is) != CF_SPECIAL_COUNT)
544 throw SerializationError("unsupported CF_SPECIAL_COUNT");
545 for (TileDef &td : tiledef_special)
546 td.deSerialize(is, version, drawtype);
548 color.setRed(readU8(is));
549 color.setGreen(readU8(is));
550 color.setBlue(readU8(is));
551 palette_name = deSerializeString16(is);
553 connect_sides = readU8(is);
554 u16 connects_to_size = readU16(is);
555 connects_to_ids.clear();
556 for (u16 i = 0; i < connects_to_size; i++)
557 connects_to_ids.push_back(readU16(is));
558 post_effect_color = readARGB8(is);
559 leveled = readU8(is);
562 light_propagates = readU8(is);
563 sunlight_propagates = readU8(is);
564 light_source = readU8(is);
565 light_source = MYMIN(light_source, LIGHT_MAX);
568 is_ground_content = readU8(is);
571 walkable = readU8(is);
572 pointable = readU8(is);
573 diggable = readU8(is);
574 climbable = readU8(is);
575 buildable_to = readU8(is);
576 rightclickable = readU8(is);
577 damage_per_second = readU32(is);
580 liquid_type = (enum LiquidType) readU8(is);
581 liquid_alternative_flowing = deSerializeString16(is);
582 liquid_alternative_source = deSerializeString16(is);
583 liquid_viscosity = readU8(is);
584 liquid_renewable = readU8(is);
585 liquid_range = readU8(is);
586 drowning = readU8(is);
587 floodable = readU8(is);
590 node_box.deSerialize(is);
591 selection_box.deSerialize(is);
592 collision_box.deSerialize(is);
595 sound_footstep.deSerialize(is, version);
596 sound_dig.deSerialize(is, version);
597 sound_dug.deSerialize(is, version);
599 // read legacy properties
600 legacy_facedir_simple = readU8(is);
601 legacy_wallmounted = readU8(is);
604 node_dig_prediction = deSerializeString16(is);
605 u8 tmp_leveled_max = readU8(is);
606 if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
607 throw SerializationError("");
608 leveled_max = tmp_leveled_max;
609 } catch(SerializationError &e) {};
613 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
614 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
615 u8 material_type, u32 shader_id, bool backface_culling,
616 const TextureSettings &tsettings)
618 layer->shader_id = shader_id;
619 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
620 layer->material_type = material_type;
622 bool has_scale = tiledef.scale > 0;
623 bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
624 (tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
625 if (use_autoscale && layer->texture) {
626 auto texture_size = layer->texture->getOriginalSize();
627 float base_size = tsettings.node_texture_size;
628 float size = std::fmin(texture_size.Width, texture_size.Height);
629 layer->scale = std::fmax(base_size, size) / base_size;
630 } else if (has_scale) {
631 layer->scale = tiledef.scale;
635 if (!tile.world_aligned)
638 // Normal texture and shader flags texture
639 if (tsettings.use_normal_texture) {
640 layer->normal_texture = tsrc->getNormalTexture(tiledef.name);
642 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
645 layer->material_flags = 0;
646 if (backface_culling)
647 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
648 if (tiledef.animation.type != TAT_NONE)
649 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
650 if (tiledef.tileable_horizontal)
651 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
652 if (tiledef.tileable_vertical)
653 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
656 layer->has_color = tiledef.has_color;
657 if (tiledef.has_color)
658 layer->color = tiledef.color;
660 layer->color = color;
662 // Animation parameters
664 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
666 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
667 &frame_count, &frame_length_ms, NULL);
668 layer->animation_frame_count = frame_count;
669 layer->animation_frame_length_ms = frame_length_ms;
672 if (frame_count == 1) {
673 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
675 std::ostringstream os(std::ios::binary);
676 if (!layer->frames) {
677 layer->frames = new std::vector<FrameSpec>();
679 layer->frames->resize(frame_count);
681 for (int i = 0; i < frame_count; i++) {
687 tiledef.animation.getTextureModifer(os,
688 layer->texture->getOriginalSize(), i);
690 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
691 if (layer->normal_texture)
692 frame.normal_texture = tsrc->getNormalTexture(os.str());
693 frame.flags_texture = layer->flags_texture;
694 (*layer->frames)[i] = frame;
699 bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length)
701 video::IVideoDriver *driver = RenderingEngine::get_video_driver();
702 static thread_local bool long_warning_printed = false;
703 std::set<std::string> seen;
704 for (int i = 0; i < length; i++) {
705 if (seen.find(tiles[i].name) != seen.end())
707 seen.insert(tiles[i].name);
709 // Load the texture and see if there's any transparent pixels
710 video::ITexture *texture = tsrc->getTexture(tiles[i].name);
711 video::IImage *image = driver->createImage(texture,
712 core::position2d<s32>(0, 0), texture->getOriginalSize());
715 core::dimension2d<u32> dim = image->getDimension();
717 for (u16 x = 0; x < dim.Width; x++) {
718 for (u16 y = 0; y < dim.Height; y++) {
719 if (image->getPixel(x, y).getAlpha() < 255) {
729 warningstream << "Texture \"" << tiles[i].name << "\" of "
730 << name << " has transparent pixels, assuming "
731 "use_texture_alpha = true." << std::endl;
732 if (!long_warning_printed) {
733 warningstream << " This warning can be a false-positive if "
734 "unused pixels in the texture are transparent. However if "
735 "it is meant to be transparent, you *MUST* update the "
736 "nodedef and set use_texture_alpha = true! This compatibility "
737 "code will be removed in a few releases." << std::endl;
738 long_warning_printed = true;
746 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
748 if (style == ALIGN_STYLE_WORLD)
750 if (mode == WORLDALIGN_DISABLE)
752 if (style == ALIGN_STYLE_USER_DEFINED)
754 if (drawtype == NDT_NORMAL)
755 return mode >= WORLDALIGN_FORCE;
756 if (drawtype == NDT_NODEBOX)
757 return mode >= WORLDALIGN_FORCE_NODEBOX;
761 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
762 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
764 // minimap pixel color - the average color of a texture
765 if (tsettings.enable_minimap && !tiledef[0].name.empty())
766 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
768 // Figure out the actual tiles to use
770 for (u32 j = 0; j < 6; j++) {
771 tdef[j] = tiledef[j];
772 if (tdef[j].name.empty())
773 tdef[j].name = "unknown_node.png";
775 // also the overlay tiles
776 TileDef tdef_overlay[6];
777 for (u32 j = 0; j < 6; j++)
778 tdef_overlay[j] = tiledef_overlay[j];
779 // also the special tiles
780 TileDef tdef_spec[6];
781 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
782 tdef_spec[j] = tiledef_special[j];
784 bool is_liquid = false;
786 u8 material_type = (alpha == 255) ?
787 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
792 material_type = (alpha == 255) ?
793 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
800 assert(liquid_type == LIQUID_SOURCE);
801 if (tsettings.opaque_water)
806 case NDT_FLOWINGLIQUID:
807 assert(liquid_type == LIQUID_FLOWING);
809 if (tsettings.opaque_water)
815 visual_solidness = 1;
817 case NDT_GLASSLIKE_FRAMED:
819 visual_solidness = 1;
821 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
823 visual_solidness = 1;
824 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
828 visual_solidness = 1;
830 case NDT_ALLFACES_OPTIONAL:
831 if (tsettings.leaves_style == LEAVES_FANCY) {
832 drawtype = NDT_ALLFACES;
834 visual_solidness = 1;
835 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
836 for (u32 j = 0; j < 6; j++) {
837 if (!tdef_spec[j].name.empty())
838 tdef[j].name = tdef_spec[j].name;
840 drawtype = NDT_GLASSLIKE;
842 visual_solidness = 1;
844 drawtype = NDT_NORMAL;
846 for (TileDef &td : tdef)
847 td.name += std::string("^[noalpha");
850 material_type = TILE_MATERIAL_WAVING_LEAVES;
855 material_type = TILE_MATERIAL_WAVING_PLANTS;
862 if (alpha == 255 && textureAlphaCheck(tsrc, tdef, 6))
867 material_type = TILE_MATERIAL_WAVING_PLANTS;
868 else if (waving == 2)
869 material_type = TILE_MATERIAL_WAVING_LEAVES;
870 else if (waving == 3)
871 material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE :
872 TILE_MATERIAL_WAVING_LIQUID_BASIC;
873 else if (alpha == 255)
874 material_type = TILE_MATERIAL_OPAQUE;
882 case NDT_PLANTLIKE_ROOTED:
888 // Vertex alpha is no longer supported, correct if necessary.
889 correctAlpha(tdef, 6);
890 correctAlpha(tdef_overlay, 6);
891 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
894 material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE :
895 TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT;
897 material_type = (alpha == 255) ? TILE_MATERIAL_LIQUID_OPAQUE :
898 TILE_MATERIAL_LIQUID_TRANSPARENT;
902 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
904 u8 overlay_material = material_type;
905 if (overlay_material == TILE_MATERIAL_OPAQUE)
906 overlay_material = TILE_MATERIAL_BASIC;
907 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
908 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
910 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
912 // Tiles (fill in f->tiles[])
913 for (u16 j = 0; j < 6; j++) {
914 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
915 tsettings.world_aligned_mode, drawtype);
916 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
917 color, material_type, tile_shader,
918 tdef[j].backface_culling, tsettings);
919 if (!tdef_overlay[j].name.empty())
920 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
921 color, overlay_material, overlay_shader,
922 tdef[j].backface_culling, tsettings);
925 u8 special_material = material_type;
926 if (drawtype == NDT_PLANTLIKE_ROOTED) {
928 special_material = TILE_MATERIAL_WAVING_PLANTS;
929 else if (waving == 2)
930 special_material = TILE_MATERIAL_WAVING_LEAVES;
932 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
934 // Special tiles (fill in f->special_tiles[])
935 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
936 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
937 color, special_material, special_shader,
938 tdef_spec[j].backface_culling, tsettings);
940 if (param_type_2 == CPT2_COLOR ||
941 param_type_2 == CPT2_COLORED_FACEDIR ||
942 param_type_2 == CPT2_COLORED_WALLMOUNTED)
943 palette = tsrc->getPalette(palette_name);
945 if (drawtype == NDT_MESH && !mesh.empty()) {
947 // Read the mesh and apply scale
948 mesh_ptr[0] = client->getMesh(mesh);
950 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
951 scaleMesh(mesh_ptr[0], scale);
952 recalculateBoundingBox(mesh_ptr[0]);
953 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
957 //Cache 6dfacedir and wallmounted rotated clones of meshes
958 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
959 (param_type_2 == CPT2_FACEDIR
960 || param_type_2 == CPT2_COLORED_FACEDIR)) {
961 for (u16 j = 1; j < 24; j++) {
962 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
963 rotateMeshBy6dFacedir(mesh_ptr[j], j);
964 recalculateBoundingBox(mesh_ptr[j]);
965 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
967 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
968 && (param_type_2 == CPT2_WALLMOUNTED ||
969 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
970 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
971 for (u16 j = 1; j < 6; j++) {
972 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
973 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
974 recalculateBoundingBox(mesh_ptr[j]);
975 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
977 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
978 recalculateBoundingBox(mesh_ptr[0]);
979 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
991 NodeDefManager::NodeDefManager()
997 NodeDefManager::~NodeDefManager()
1000 for (ContentFeatures &f : m_content_features) {
1001 for (auto &j : f.mesh_ptr) {
1010 void NodeDefManager::clear()
1012 m_content_features.clear();
1013 m_name_id_mapping.clear();
1014 m_name_id_mapping_with_aliases.clear();
1015 m_group_to_items.clear();
1017 m_selection_box_union.reset(0,0,0);
1018 m_selection_box_int_union.reset(0,0,0);
1020 resetNodeResolveState();
1022 u32 initial_length = 0;
1023 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1024 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1025 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1026 m_content_features.resize(initial_length);
1028 // Set CONTENT_UNKNOWN
1032 // Insert directly into containers
1033 content_t c = CONTENT_UNKNOWN;
1034 m_content_features[c] = f;
1035 addNameIdMapping(c, f.name);
1042 f.drawtype = NDT_AIRLIKE;
1043 f.param_type = CPT_LIGHT;
1044 f.light_propagates = true;
1045 f.sunlight_propagates = true;
1047 f.pointable = false;
1049 f.buildable_to = true;
1051 f.is_ground_content = true;
1052 // Insert directly into containers
1053 content_t c = CONTENT_AIR;
1054 m_content_features[c] = f;
1055 addNameIdMapping(c, f.name);
1058 // Set CONTENT_IGNORE
1062 f.drawtype = NDT_AIRLIKE;
1063 f.param_type = CPT_NONE;
1064 f.light_propagates = false;
1065 f.sunlight_propagates = false;
1067 f.pointable = false;
1069 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1070 f.is_ground_content = true;
1071 // Insert directly into containers
1072 content_t c = CONTENT_IGNORE;
1073 m_content_features[c] = f;
1074 addNameIdMapping(c, f.name);
1079 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1081 std::unordered_map<std::string, content_t>::const_iterator
1082 i = m_name_id_mapping_with_aliases.find(name);
1083 if(i == m_name_id_mapping_with_aliases.end())
1090 content_t NodeDefManager::getId(const std::string &name) const
1092 content_t id = CONTENT_IGNORE;
1098 bool NodeDefManager::getIds(const std::string &name,
1099 std::vector<content_t> &result) const
1101 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1102 if (name.substr(0,6) != "group:") {
1103 content_t id = CONTENT_IGNORE;
1104 bool exists = getId(name, id);
1106 result.push_back(id);
1109 std::string group = name.substr(6);
1111 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1112 i = m_group_to_items.find(group);
1113 if (i == m_group_to_items.end())
1116 const std::vector<content_t> &items = i->second;
1117 result.insert(result.end(), items.begin(), items.end());
1118 //printf("getIds: %dus\n", t.stop());
1123 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1125 content_t id = CONTENT_UNKNOWN;
1131 // returns CONTENT_IGNORE if no free ID found
1132 content_t NodeDefManager::allocateId()
1134 for (content_t id = m_next_id;
1135 id >= m_next_id; // overflow?
1137 while (id >= m_content_features.size()) {
1138 m_content_features.emplace_back();
1140 const ContentFeatures &f = m_content_features[id];
1141 if (f.name.empty()) {
1146 // If we arrive here, an overflow occurred in id.
1147 // That means no ID was found
1148 return CONTENT_IGNORE;
1153 * Returns the smallest box that contains all boxes
1154 * in the vector. Box_union is expanded.
1155 * @param[in] boxes the vector containing the boxes
1156 * @param[in, out] box_union the union of the arguments
1158 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1160 for (const aabb3f &box : boxes) {
1161 box_union->addInternalBox(box);
1167 * Returns a box that contains the nodebox in every case.
1168 * The argument node_union is expanded.
1169 * @param[in] nodebox the nodebox to be measured
1170 * @param[in] features used to decide whether the nodebox
1172 * @param[in, out] box_union the union of the arguments
1174 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1177 switch(nodebox.type) {
1179 case NODEBOX_LEVELED: {
1181 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1182 boxVectorUnion(nodebox.fixed, &half_processed);
1183 // Set leveled boxes to maximal
1184 if (nodebox.type == NODEBOX_LEVELED) {
1185 half_processed.MaxEdge.Y = +BS / 2;
1187 if (features.param_type_2 == CPT2_FACEDIR ||
1188 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1189 // Get maximal coordinate
1191 fabsf(half_processed.MinEdge.X),
1192 fabsf(half_processed.MinEdge.Y),
1193 fabsf(half_processed.MinEdge.Z),
1194 fabsf(half_processed.MaxEdge.X),
1195 fabsf(half_processed.MaxEdge.Y),
1196 fabsf(half_processed.MaxEdge.Z) };
1198 for (float coord : coords) {
1203 // Add the union of all possible rotated boxes
1204 box_union->addInternalPoint(-max, -max, -max);
1205 box_union->addInternalPoint(+max, +max, +max);
1207 box_union->addInternalBox(half_processed);
1211 case NODEBOX_WALLMOUNTED: {
1213 box_union->addInternalBox(nodebox.wall_top);
1214 box_union->addInternalBox(nodebox.wall_bottom);
1215 // Find maximal coordinate in the X-Z plane
1217 fabsf(nodebox.wall_side.MinEdge.X),
1218 fabsf(nodebox.wall_side.MinEdge.Z),
1219 fabsf(nodebox.wall_side.MaxEdge.X),
1220 fabsf(nodebox.wall_side.MaxEdge.Z) };
1222 for (float coord : coords) {
1227 // Add the union of all possible rotated boxes
1228 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1229 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1232 case NODEBOX_CONNECTED: {
1233 // Add all possible connected boxes
1234 boxVectorUnion(nodebox.fixed, box_union);
1235 boxVectorUnion(nodebox.connect_top, box_union);
1236 boxVectorUnion(nodebox.connect_bottom, box_union);
1237 boxVectorUnion(nodebox.connect_front, box_union);
1238 boxVectorUnion(nodebox.connect_left, box_union);
1239 boxVectorUnion(nodebox.connect_back, box_union);
1240 boxVectorUnion(nodebox.connect_right, box_union);
1241 boxVectorUnion(nodebox.disconnected_top, box_union);
1242 boxVectorUnion(nodebox.disconnected_bottom, box_union);
1243 boxVectorUnion(nodebox.disconnected_front, box_union);
1244 boxVectorUnion(nodebox.disconnected_left, box_union);
1245 boxVectorUnion(nodebox.disconnected_back, box_union);
1246 boxVectorUnion(nodebox.disconnected_right, box_union);
1247 boxVectorUnion(nodebox.disconnected, box_union);
1248 boxVectorUnion(nodebox.disconnected_sides, box_union);
1253 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1254 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1260 inline void NodeDefManager::fixSelectionBoxIntUnion()
1262 m_selection_box_int_union.MinEdge.X = floorf(
1263 m_selection_box_union.MinEdge.X / BS + 0.5f);
1264 m_selection_box_int_union.MinEdge.Y = floorf(
1265 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1266 m_selection_box_int_union.MinEdge.Z = floorf(
1267 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1268 m_selection_box_int_union.MaxEdge.X = ceilf(
1269 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1270 m_selection_box_int_union.MaxEdge.Y = ceilf(
1271 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1272 m_selection_box_int_union.MaxEdge.Z = ceilf(
1273 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1277 void NodeDefManager::eraseIdFromGroups(content_t id)
1279 // For all groups in m_group_to_items...
1280 for (auto iter_groups = m_group_to_items.begin();
1281 iter_groups != m_group_to_items.end();) {
1282 // Get the group items vector.
1283 std::vector<content_t> &items = iter_groups->second;
1285 // Remove any occurence of the id in the group items vector.
1286 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1288 // If group is empty, erase its vector from the map.
1290 iter_groups = m_group_to_items.erase(iter_groups);
1297 // IWritableNodeDefManager
1298 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1302 assert(name != "ignore");
1303 assert(name == def.name);
1305 content_t id = CONTENT_IGNORE;
1306 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1309 if (id == CONTENT_IGNORE) {
1310 warningstream << "NodeDefManager: Absolute "
1311 "limit reached" << std::endl;
1312 return CONTENT_IGNORE;
1314 assert(id != CONTENT_IGNORE);
1315 addNameIdMapping(id, name);
1318 // If there is already ContentFeatures registered for this id, clear old groups
1319 if (id < m_content_features.size())
1320 eraseIdFromGroups(id);
1322 m_content_features[id] = def;
1323 verbosestream << "NodeDefManager: registering content id \"" << id
1324 << "\": name=\"" << def.name << "\""<<std::endl;
1326 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1327 fixSelectionBoxIntUnion();
1329 // Add this content to the list of all groups it belongs to
1330 for (const auto &group : def.groups) {
1331 const std::string &group_name = group.first;
1332 m_group_to_items[group_name].push_back(id);
1339 content_t NodeDefManager::allocateDummy(const std::string &name)
1341 assert(name != ""); // Pre-condition
1344 return set(name, f);
1348 void NodeDefManager::removeNode(const std::string &name)
1353 // Erase name from name ID mapping
1354 content_t id = CONTENT_IGNORE;
1355 if (m_name_id_mapping.getId(name, id)) {
1356 m_name_id_mapping.eraseName(name);
1357 m_name_id_mapping_with_aliases.erase(name);
1360 eraseIdFromGroups(id);
1364 void NodeDefManager::updateAliases(IItemDefManager *idef)
1366 std::set<std::string> all;
1368 m_name_id_mapping_with_aliases.clear();
1369 for (const std::string &name : all) {
1370 const std::string &convert_to = idef->getAlias(name);
1372 if (m_name_id_mapping.getId(convert_to, id)) {
1373 m_name_id_mapping_with_aliases.insert(
1374 std::make_pair(name, id));
1379 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1381 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1382 "overrides to textures" << std::endl;
1384 for (const TextureOverride& texture_override : overrides) {
1386 if (!getId(texture_override.id, id))
1387 continue; // Ignore unknown node
1389 ContentFeatures &nodedef = m_content_features[id];
1392 if (texture_override.hasTarget(OverrideTarget::TOP))
1393 nodedef.tiledef[0].name = texture_override.texture;
1395 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1396 nodedef.tiledef[1].name = texture_override.texture;
1398 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1399 nodedef.tiledef[2].name = texture_override.texture;
1401 if (texture_override.hasTarget(OverrideTarget::LEFT))
1402 nodedef.tiledef[3].name = texture_override.texture;
1404 if (texture_override.hasTarget(OverrideTarget::BACK))
1405 nodedef.tiledef[4].name = texture_override.texture;
1407 if (texture_override.hasTarget(OverrideTarget::FRONT))
1408 nodedef.tiledef[5].name = texture_override.texture;
1411 // Override special tiles, if applicable
1412 if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1413 nodedef.tiledef_special[0].name = texture_override.texture;
1415 if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1416 nodedef.tiledef_special[1].name = texture_override.texture;
1418 if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1419 nodedef.tiledef_special[2].name = texture_override.texture;
1421 if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1422 nodedef.tiledef_special[3].name = texture_override.texture;
1424 if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1425 nodedef.tiledef_special[4].name = texture_override.texture;
1427 if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1428 nodedef.tiledef_special[5].name = texture_override.texture;
1432 void NodeDefManager::updateTextures(IGameDef *gamedef,
1433 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1434 void *progress_callback_args)
1437 infostream << "NodeDefManager::updateTextures(): Updating "
1438 "textures in node definitions" << std::endl;
1440 Client *client = (Client *)gamedef;
1441 ITextureSource *tsrc = client->tsrc();
1442 IShaderSource *shdsrc = client->getShaderSource();
1443 scene::IMeshManipulator *meshmanip =
1444 RenderingEngine::get_scene_manager()->getMeshManipulator();
1445 TextureSettings tsettings;
1446 tsettings.readSettings();
1448 u32 size = m_content_features.size();
1450 for (u32 i = 0; i < size; i++) {
1451 ContentFeatures *f = &(m_content_features[i]);
1452 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1453 progress_callback(progress_callback_args, i, size);
1458 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1460 writeU8(os, 1); // version
1462 std::ostringstream os2(std::ios::binary);
1463 for (u32 i = 0; i < m_content_features.size(); i++) {
1464 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1465 || i == CONTENT_UNKNOWN)
1467 const ContentFeatures *f = &m_content_features[i];
1468 if (f->name.empty())
1471 // Wrap it in a string to allow different lengths without
1472 // strict version incompatibilities
1473 std::ostringstream wrapper_os(std::ios::binary);
1474 f->serialize(wrapper_os, protocol_version);
1475 os2<<serializeString16(wrapper_os.str());
1477 // must not overflow
1478 u16 next = count + 1;
1479 FATAL_ERROR_IF(next < count, "Overflow");
1482 writeU16(os, count);
1483 os << serializeString32(os2.str());
1487 void NodeDefManager::deSerialize(std::istream &is)
1490 int version = readU8(is);
1492 throw SerializationError("unsupported NodeDefinitionManager version");
1493 u16 count = readU16(is);
1494 std::istringstream is2(deSerializeString32(is), std::ios::binary);
1496 for (u16 n = 0; n < count; n++) {
1497 u16 i = readU16(is2);
1499 // Read it from the string wrapper
1500 std::string wrapper = deSerializeString16(is2);
1501 std::istringstream wrapper_is(wrapper, std::ios::binary);
1502 f.deSerialize(wrapper_is);
1504 // Check error conditions
1505 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1506 warningstream << "NodeDefManager::deSerialize(): "
1507 "not changing builtin node " << i << std::endl;
1510 if (f.name.empty()) {
1511 warningstream << "NodeDefManager::deSerialize(): "
1512 "received empty name" << std::endl;
1518 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1519 warningstream << "NodeDefManager::deSerialize(): "
1520 "already defined with different ID: " << f.name << std::endl;
1524 // All is ok, add node definition with the requested ID
1525 if (i >= m_content_features.size())
1526 m_content_features.resize((u32)(i) + 1);
1527 m_content_features[i] = f;
1528 addNameIdMapping(i, f.name);
1529 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1531 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1532 fixSelectionBoxIntUnion();
1535 // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1536 // are not sent, resolve them client-side too.
1541 void NodeDefManager::addNameIdMapping(content_t i, std::string name)
1543 m_name_id_mapping.set(i, name);
1544 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1548 NodeDefManager *createNodeDefManager()
1550 return new NodeDefManager();
1554 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1557 if (m_node_registration_complete)
1558 nr->nodeResolveInternal();
1560 m_pending_resolve_callbacks.push_back(nr);
1564 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1566 size_t len = m_pending_resolve_callbacks.size();
1567 for (size_t i = 0; i != len; i++) {
1568 if (nr != m_pending_resolve_callbacks[i])
1572 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1573 m_pending_resolve_callbacks.resize(len);
1581 void NodeDefManager::runNodeResolveCallbacks()
1583 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1584 NodeResolver *nr = m_pending_resolve_callbacks[i];
1585 nr->nodeResolveInternal();
1588 m_pending_resolve_callbacks.clear();
1592 void NodeDefManager::resetNodeResolveState()
1594 m_node_registration_complete = false;
1595 m_pending_resolve_callbacks.clear();
1598 static void removeDupes(std::vector<content_t> &list)
1600 std::sort(list.begin(), list.end());
1601 auto new_end = std::unique(list.begin(), list.end());
1602 list.erase(new_end, list.end());
1605 void NodeDefManager::resolveCrossrefs()
1607 for (ContentFeatures &f : m_content_features) {
1608 if (f.liquid_type != LIQUID_NONE) {
1609 f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1610 f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1613 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1616 for (const std::string &name : f.connects_to) {
1617 getIds(name, f.connects_to_ids);
1619 removeDupes(f.connects_to_ids);
1623 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1624 u8 connect_face) const
1626 const ContentFeatures &f1 = get(from);
1628 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1631 // lookup target in connected set
1632 if (!CONTAINS(f1.connects_to_ids, to.param0))
1635 const ContentFeatures &f2 = get(to);
1637 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1638 // ignores actually looking if back connection exists
1639 return CONTAINS(f2.connects_to_ids, from.param0);
1641 // does to node declare usable faces?
1642 if (f2.connect_sides > 0) {
1643 if ((f2.param_type_2 == CPT2_FACEDIR ||
1644 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1645 && (connect_face >= 4)) {
1646 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1647 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1649 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1651 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1652 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1654 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1655 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1656 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1658 return (f2.connect_sides
1659 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1661 return (f2.connect_sides & connect_face);
1663 // the target is just a regular node, so connect no matter back connection
1671 NodeResolver::NodeResolver()
1673 m_nodenames.reserve(16);
1674 m_nnlistsizes.reserve(4);
1678 NodeResolver::~NodeResolver()
1680 if (!m_resolve_done && m_ndef)
1681 m_ndef->cancelNodeResolveCallback(this);
1685 void NodeResolver::cloneTo(NodeResolver *res) const
1687 FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1688 " after resolving has completed");
1689 /* We don't actually do anything significant. Since the node resolving has
1690 * already completed, the class that called us will already have the
1691 * resolved IDs in its data structures (which it copies on its own) */
1692 res->m_ndef = m_ndef;
1693 res->m_resolve_done = true;
1697 void NodeResolver::nodeResolveInternal()
1699 m_nodenames_idx = 0;
1700 m_nnlistsizes_idx = 0;
1703 m_resolve_done = true;
1705 m_nodenames.clear();
1706 m_nnlistsizes.clear();
1710 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1711 const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1713 if (m_nodenames_idx == m_nodenames.size()) {
1714 *result_out = c_fallback;
1715 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1720 std::string name = m_nodenames[m_nodenames_idx++];
1722 bool success = m_ndef->getId(name, c);
1723 if (!success && !node_alt.empty()) {
1725 success = m_ndef->getId(name, c);
1729 if (error_on_fallback)
1730 errorstream << "NodeResolver: failed to resolve node name '" << name
1731 << "'." << std::endl;
1740 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1741 bool all_required, content_t c_fallback)
1743 bool success = true;
1745 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1746 errorstream << "NodeResolver: no more node lists" << std::endl;
1750 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1753 if (m_nodenames_idx == m_nodenames.size()) {
1754 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1759 std::string &name = m_nodenames[m_nodenames_idx++];
1761 if (name.substr(0,6) != "group:") {
1762 if (m_ndef->getId(name, c)) {
1763 result_out->push_back(c);
1764 } else if (all_required) {
1765 errorstream << "NodeResolver: failed to resolve node name '"
1766 << name << "'." << std::endl;
1767 result_out->push_back(c_fallback);
1771 m_ndef->getIds(name, *result_out);