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.
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 writeV3F1000(os, nodebox.MinEdge);
88 writeV3F1000(os, nodebox.MaxEdge);
91 case NODEBOX_WALLMOUNTED:
94 writeV3F1000(os, wall_top.MinEdge);
95 writeV3F1000(os, wall_top.MaxEdge);
96 writeV3F1000(os, wall_bottom.MinEdge);
97 writeV3F1000(os, wall_bottom.MaxEdge);
98 writeV3F1000(os, wall_side.MinEdge);
99 writeV3F1000(os, wall_side.MaxEdge);
101 case NODEBOX_CONNECTED:
104 #define WRITEBOX(box) \
105 writeU16(os, (box).size()); \
106 for (const aabb3f &i: (box)) { \
107 writeV3F1000(os, i.MinEdge); \
108 writeV3F1000(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 = readV3F1000(is);
150 box.MaxEdge = readV3F1000(is);
151 fixed.push_back(box);
154 else if(type == NODEBOX_WALLMOUNTED)
156 wall_top.MinEdge = readV3F1000(is);
157 wall_top.MaxEdge = readV3F1000(is);
158 wall_bottom.MinEdge = readV3F1000(is);
159 wall_bottom.MaxEdge = readV3F1000(is);
160 wall_side.MinEdge = readV3F1000(is);
161 wall_side.MaxEdge = readV3F1000(is);
163 else if (type == NODEBOX_CONNECTED)
165 #define READBOXES(box) { \
166 count = readU16(is); \
167 (box).reserve(count); \
169 v3f min = readV3F1000(is); \
170 v3f max = readV3F1000(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);
183 READBOXES(disconnected_top);
184 READBOXES(disconnected_bottom);
185 READBOXES(disconnected_front);
186 READBOXES(disconnected_left);
187 READBOXES(disconnected_back);
188 READBOXES(disconnected_right);
189 READBOXES(disconnected);
190 READBOXES(disconnected_sides);
199 #define TILE_FLAG_BACKFACE_CULLING (1 << 0)
200 #define TILE_FLAG_TILEABLE_HORIZONTAL (1 << 1)
201 #define TILE_FLAG_TILEABLE_VERTICAL (1 << 2)
202 #define TILE_FLAG_HAS_COLOR (1 << 3)
203 #define TILE_FLAG_HAS_SCALE (1 << 4)
204 #define TILE_FLAG_HAS_ALIGN_STYLE (1 << 5)
206 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
208 // protocol_version >= 36
210 writeU8(os, version);
212 os << serializeString(name);
213 animation.serialize(os, version);
214 bool has_scale = scale > 0;
216 if (backface_culling)
217 flags |= TILE_FLAG_BACKFACE_CULLING;
218 if (tileable_horizontal)
219 flags |= TILE_FLAG_TILEABLE_HORIZONTAL;
220 if (tileable_vertical)
221 flags |= TILE_FLAG_TILEABLE_VERTICAL;
223 flags |= TILE_FLAG_HAS_COLOR;
225 flags |= TILE_FLAG_HAS_SCALE;
226 if (align_style != ALIGN_STYLE_NODE)
227 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
230 writeU8(os, color.getRed());
231 writeU8(os, color.getGreen());
232 writeU8(os, color.getBlue());
236 if (align_style != ALIGN_STYLE_NODE)
237 writeU8(os, align_style);
240 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
241 NodeDrawType drawtype)
243 int version = readU8(is);
245 throw SerializationError("unsupported TileDef version");
246 name = deSerializeString(is);
247 animation.deSerialize(is, version);
248 u16 flags = readU16(is);
249 backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
250 tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL;
251 tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL;
252 has_color = flags & TILE_FLAG_HAS_COLOR;
253 bool has_scale = flags & TILE_FLAG_HAS_SCALE;
254 bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE;
256 color.setRed(readU8(is));
257 color.setGreen(readU8(is));
258 color.setBlue(readU8(is));
260 scale = has_scale ? readU8(is) : 0;
262 align_style = static_cast<AlignStyle>(readU8(is));
264 align_style = ALIGN_STYLE_NODE;
269 SimpleSoundSpec serialization
272 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
273 std::ostream &os, u8 version)
275 os<<serializeString(ss.name);
276 writeF1000(os, ss.gain);
277 writeF1000(os, ss.pitch);
279 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss,
280 std::istream &is, u8 version)
282 ss.name = deSerializeString(is);
283 ss.gain = readF1000(is);
284 ss.pitch = readF1000(is);
287 void TextureSettings::readSettings()
289 connected_glass = g_settings->getBool("connected_glass");
290 opaque_water = g_settings->getBool("opaque_water");
291 bool enable_shaders = g_settings->getBool("enable_shaders");
292 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
293 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
294 bool smooth_lighting = g_settings->getBool("smooth_lighting");
295 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
296 enable_minimap = g_settings->getBool("enable_minimap");
297 node_texture_size = g_settings->getU16("texture_min_size");
298 std::string leaves_style_str = g_settings->get("leaves_style");
299 std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
300 std::string autoscale_mode_str = g_settings->get("autoscale_mode");
302 // Mesh cache is not supported in combination with smooth lighting
304 enable_mesh_cache = false;
306 use_normal_texture = enable_shaders &&
307 (enable_bumpmapping || enable_parallax_occlusion);
308 if (leaves_style_str == "fancy") {
309 leaves_style = LEAVES_FANCY;
310 } else if (leaves_style_str == "simple") {
311 leaves_style = LEAVES_SIMPLE;
313 leaves_style = LEAVES_OPAQUE;
316 if (world_aligned_mode_str == "enable")
317 world_aligned_mode = WORLDALIGN_ENABLE;
318 else if (world_aligned_mode_str == "force_solid")
319 world_aligned_mode = WORLDALIGN_FORCE;
320 else if (world_aligned_mode_str == "force_nodebox")
321 world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
323 world_aligned_mode = WORLDALIGN_DISABLE;
325 if (autoscale_mode_str == "enable")
326 autoscale_mode = AUTOSCALE_ENABLE;
327 else if (autoscale_mode_str == "force")
328 autoscale_mode = AUTOSCALE_FORCE;
330 autoscale_mode = AUTOSCALE_DISABLE;
337 ContentFeatures::ContentFeatures()
342 void ContentFeatures::reset()
349 visual_solidness = 0;
350 backface_culling = true;
353 has_on_construct = false;
354 has_on_destruct = false;
355 has_after_destruct = false;
359 NOTE: Most of this is always overridden by the default values given
364 // Unknown nodes can be dug
365 groups["dig_immediate"] = 2;
366 drawtype = NDT_NORMAL;
369 for (auto &i : mesh_ptr)
371 minimap_color = video::SColor(0, 0, 0, 0);
374 for (auto &i : tiledef)
376 for (auto &j : tiledef_special)
379 post_effect_color = video::SColor(0, 0, 0, 0);
380 param_type = CPT_NONE;
381 param_type_2 = CPT2_NONE;
382 is_ground_content = false;
383 light_propagates = false;
384 sunlight_propagates = false;
389 buildable_to = false;
391 rightclickable = true;
393 liquid_type = LIQUID_NONE;
394 liquid_alternative_flowing = "";
395 liquid_alternative_source = "";
396 liquid_viscosity = 0;
397 liquid_renewable = true;
398 liquid_range = LIQUID_LEVEL_MAX+1;
401 damage_per_second = 0;
402 node_box = NodeBox();
403 selection_box = NodeBox();
404 collision_box = NodeBox();
406 legacy_facedir_simple = false;
407 legacy_wallmounted = false;
408 sound_footstep = SimpleSoundSpec();
409 sound_dig = SimpleSoundSpec("__group");
410 sound_dug = SimpleSoundSpec();
412 connects_to_ids.clear();
414 color = video::SColor(0xFFFFFFFF);
417 node_dig_prediction = "air";
420 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
422 // protocol_version >= 36
424 writeU8(os, version);
427 os << serializeString(name);
428 writeU16(os, groups.size());
429 for (const auto &group : groups) {
430 os << serializeString(group.first);
431 writeS16(os, group.second);
433 writeU8(os, param_type);
434 writeU8(os, param_type_2);
437 writeU8(os, drawtype);
438 os << serializeString(mesh);
439 writeF1000(os, visual_scale);
441 for (const TileDef &td : tiledef)
442 td.serialize(os, protocol_version);
443 for (const TileDef &td : tiledef_overlay)
444 td.serialize(os, protocol_version);
445 writeU8(os, CF_SPECIAL_COUNT);
446 for (const TileDef &td : tiledef_special) {
447 td.serialize(os, protocol_version);
450 writeU8(os, color.getRed());
451 writeU8(os, color.getGreen());
452 writeU8(os, color.getBlue());
453 os << serializeString(palette_name);
455 writeU8(os, connect_sides);
456 writeU16(os, connects_to_ids.size());
457 for (u16 connects_to_id : connects_to_ids)
458 writeU16(os, connects_to_id);
459 writeU8(os, post_effect_color.getAlpha());
460 writeU8(os, post_effect_color.getRed());
461 writeU8(os, post_effect_color.getGreen());
462 writeU8(os, post_effect_color.getBlue());
463 writeU8(os, leveled);
466 writeU8(os, light_propagates);
467 writeU8(os, sunlight_propagates);
468 writeU8(os, light_source);
471 writeU8(os, is_ground_content);
474 writeU8(os, walkable);
475 writeU8(os, pointable);
476 writeU8(os, diggable);
477 writeU8(os, climbable);
478 writeU8(os, buildable_to);
479 writeU8(os, rightclickable);
480 writeU32(os, damage_per_second);
483 writeU8(os, liquid_type);
484 os << serializeString(liquid_alternative_flowing);
485 os << serializeString(liquid_alternative_source);
486 writeU8(os, liquid_viscosity);
487 writeU8(os, liquid_renewable);
488 writeU8(os, liquid_range);
489 writeU8(os, drowning);
490 writeU8(os, floodable);
493 node_box.serialize(os, protocol_version);
494 selection_box.serialize(os, protocol_version);
495 collision_box.serialize(os, protocol_version);
498 serializeSimpleSoundSpec(sound_footstep, os, version);
499 serializeSimpleSoundSpec(sound_dig, os, version);
500 serializeSimpleSoundSpec(sound_dug, os, version);
503 writeU8(os, legacy_facedir_simple);
504 writeU8(os, legacy_wallmounted);
506 os << serializeString(node_dig_prediction);
509 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
511 // alpha == 0 means that the node is using texture alpha
512 if (alpha == 0 || alpha == 255)
515 for (int i = 0; i < length; i++) {
516 if (tiles[i].name.empty())
519 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
520 tiles[i].name = s.str();
524 void ContentFeatures::deSerialize(std::istream &is)
527 int version = readU8(is);
529 throw SerializationError("unsupported ContentFeatures version");
532 name = deSerializeString(is);
534 u32 groups_size = readU16(is);
535 for (u32 i = 0; i < groups_size; i++) {
536 std::string name = deSerializeString(is);
537 int value = readS16(is);
538 groups[name] = value;
540 param_type = (enum ContentParamType) readU8(is);
541 param_type_2 = (enum ContentParamType2) readU8(is);
544 drawtype = (enum NodeDrawType) readU8(is);
545 mesh = deSerializeString(is);
546 visual_scale = readF1000(is);
548 throw SerializationError("unsupported tile count");
549 for (TileDef &td : tiledef)
550 td.deSerialize(is, version, drawtype);
551 for (TileDef &td : tiledef_overlay)
552 td.deSerialize(is, version, drawtype);
553 if (readU8(is) != CF_SPECIAL_COUNT)
554 throw SerializationError("unsupported CF_SPECIAL_COUNT");
555 for (TileDef &td : tiledef_special)
556 td.deSerialize(is, version, drawtype);
558 color.setRed(readU8(is));
559 color.setGreen(readU8(is));
560 color.setBlue(readU8(is));
561 palette_name = deSerializeString(is);
563 connect_sides = readU8(is);
564 u16 connects_to_size = readU16(is);
565 connects_to_ids.clear();
566 for (u16 i = 0; i < connects_to_size; i++)
567 connects_to_ids.push_back(readU16(is));
568 post_effect_color.setAlpha(readU8(is));
569 post_effect_color.setRed(readU8(is));
570 post_effect_color.setGreen(readU8(is));
571 post_effect_color.setBlue(readU8(is));
572 leveled = readU8(is);
575 light_propagates = readU8(is);
576 sunlight_propagates = readU8(is);
577 light_source = readU8(is);
578 light_source = MYMIN(light_source, LIGHT_MAX);
581 is_ground_content = readU8(is);
584 walkable = readU8(is);
585 pointable = readU8(is);
586 diggable = readU8(is);
587 climbable = readU8(is);
588 buildable_to = readU8(is);
589 rightclickable = readU8(is);
590 damage_per_second = readU32(is);
593 liquid_type = (enum LiquidType) readU8(is);
594 liquid_alternative_flowing = deSerializeString(is);
595 liquid_alternative_source = deSerializeString(is);
596 liquid_viscosity = readU8(is);
597 liquid_renewable = readU8(is);
598 liquid_range = readU8(is);
599 drowning = readU8(is);
600 floodable = readU8(is);
603 node_box.deSerialize(is);
604 selection_box.deSerialize(is);
605 collision_box.deSerialize(is);
608 deSerializeSimpleSoundSpec(sound_footstep, is, version);
609 deSerializeSimpleSoundSpec(sound_dig, is, version);
610 deSerializeSimpleSoundSpec(sound_dug, is, version);
612 // read legacy properties
613 legacy_facedir_simple = readU8(is);
614 legacy_wallmounted = readU8(is);
617 node_dig_prediction = deSerializeString(is);
618 } catch(SerializationError &e) {};
622 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
623 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
624 u8 material_type, u32 shader_id, bool backface_culling,
625 const TextureSettings &tsettings)
627 layer->shader_id = shader_id;
628 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
629 layer->material_type = material_type;
631 bool has_scale = tiledef.scale > 0;
632 if (((tsettings.autoscale_mode == AUTOSCALE_ENABLE) && !has_scale) ||
633 (tsettings.autoscale_mode == AUTOSCALE_FORCE)) {
634 auto texture_size = layer->texture->getOriginalSize();
635 float base_size = tsettings.node_texture_size;
636 float size = std::fmin(texture_size.Width, texture_size.Height);
637 layer->scale = std::fmax(base_size, size) / base_size;
638 } else if (has_scale) {
639 layer->scale = tiledef.scale;
643 if (!tile.world_aligned)
646 // Normal texture and shader flags texture
647 if (tsettings.use_normal_texture) {
648 layer->normal_texture = tsrc->getNormalTexture(tiledef.name);
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 = std::make_shared<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;
709 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
711 if (style == ALIGN_STYLE_WORLD)
713 if (mode == WORLDALIGN_DISABLE)
715 if (style == ALIGN_STYLE_USER_DEFINED)
717 if (drawtype == NDT_NORMAL)
718 return mode >= WORLDALIGN_FORCE;
719 if (drawtype == NDT_NODEBOX)
720 return mode >= WORLDALIGN_FORCE_NODEBOX;
724 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
725 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
727 // minimap pixel color - the average color of a texture
728 if (tsettings.enable_minimap && !tiledef[0].name.empty())
729 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
731 // Figure out the actual tiles to use
733 for (u32 j = 0; j < 6; j++) {
734 tdef[j] = tiledef[j];
735 if (tdef[j].name.empty())
736 tdef[j].name = "unknown_node.png";
738 // also the overlay tiles
739 TileDef tdef_overlay[6];
740 for (u32 j = 0; j < 6; j++)
741 tdef_overlay[j] = tiledef_overlay[j];
742 // also the special tiles
743 TileDef tdef_spec[6];
744 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
745 tdef_spec[j] = tiledef_special[j];
747 bool is_liquid = false;
749 u8 material_type = (alpha == 255) ?
750 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
755 material_type = (alpha == 255) ?
756 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
763 assert(liquid_type == LIQUID_SOURCE);
764 if (tsettings.opaque_water)
769 case NDT_FLOWINGLIQUID:
770 assert(liquid_type == LIQUID_FLOWING);
772 if (tsettings.opaque_water)
778 visual_solidness = 1;
780 case NDT_GLASSLIKE_FRAMED:
782 visual_solidness = 1;
784 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
786 visual_solidness = 1;
787 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
791 visual_solidness = 1;
793 case NDT_ALLFACES_OPTIONAL:
794 if (tsettings.leaves_style == LEAVES_FANCY) {
795 drawtype = NDT_ALLFACES;
797 visual_solidness = 1;
798 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
799 for (u32 j = 0; j < 6; j++) {
800 if (!tdef_spec[j].name.empty())
801 tdef[j].name = tdef_spec[j].name;
803 drawtype = NDT_GLASSLIKE;
805 visual_solidness = 1;
807 drawtype = NDT_NORMAL;
809 for (TileDef &td : tdef)
810 td.name += std::string("^[noalpha");
813 material_type = TILE_MATERIAL_WAVING_LEAVES;
818 material_type = TILE_MATERIAL_WAVING_PLANTS;
827 material_type = TILE_MATERIAL_WAVING_PLANTS;
828 else if (waving == 2)
829 material_type = TILE_MATERIAL_WAVING_LEAVES;
837 case NDT_PLANTLIKE_ROOTED:
843 // Vertex alpha is no longer supported, correct if necessary.
844 correctAlpha(tdef, 6);
845 correctAlpha(tdef_overlay, 6);
846 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
847 material_type = (alpha == 255) ?
848 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
851 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
853 u8 overlay_material = material_type;
854 if (overlay_material == TILE_MATERIAL_OPAQUE)
855 overlay_material = TILE_MATERIAL_BASIC;
856 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
857 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
859 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
861 // Tiles (fill in f->tiles[])
862 for (u16 j = 0; j < 6; j++) {
863 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
864 tsettings.world_aligned_mode, drawtype);
865 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
866 color, material_type, tile_shader,
867 tdef[j].backface_culling, tsettings);
868 if (!tdef_overlay[j].name.empty())
869 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
870 color, overlay_material, overlay_shader,
871 tdef[j].backface_culling, tsettings);
874 u8 special_material = material_type;
875 if (drawtype == NDT_PLANTLIKE_ROOTED) {
877 special_material = TILE_MATERIAL_WAVING_PLANTS;
878 else if (waving == 2)
879 special_material = TILE_MATERIAL_WAVING_LEAVES;
881 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
883 // Special tiles (fill in f->special_tiles[])
884 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
885 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
886 color, special_material, special_shader,
887 tdef_spec[j].backface_culling, tsettings);
889 if (param_type_2 == CPT2_COLOR ||
890 param_type_2 == CPT2_COLORED_FACEDIR ||
891 param_type_2 == CPT2_COLORED_WALLMOUNTED)
892 palette = tsrc->getPalette(palette_name);
894 if (drawtype == NDT_MESH && !mesh.empty()) {
896 // Read the mesh and apply scale
897 mesh_ptr[0] = client->getMesh(mesh);
899 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
900 scaleMesh(mesh_ptr[0], scale);
901 recalculateBoundingBox(mesh_ptr[0]);
902 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
906 //Cache 6dfacedir and wallmounted rotated clones of meshes
907 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
908 (param_type_2 == CPT2_FACEDIR
909 || param_type_2 == CPT2_COLORED_FACEDIR)) {
910 for (u16 j = 1; j < 24; j++) {
911 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
912 rotateMeshBy6dFacedir(mesh_ptr[j], j);
913 recalculateBoundingBox(mesh_ptr[j]);
914 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
916 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
917 && (param_type_2 == CPT2_WALLMOUNTED ||
918 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
919 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
920 for (u16 j = 1; j < 6; j++) {
921 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
922 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
923 recalculateBoundingBox(mesh_ptr[j]);
924 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
926 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
927 recalculateBoundingBox(mesh_ptr[0]);
928 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
940 NodeDefManager::NodeDefManager()
946 NodeDefManager::~NodeDefManager()
949 for (ContentFeatures &f : m_content_features) {
950 for (auto &j : f.mesh_ptr) {
959 void NodeDefManager::clear()
961 m_content_features.clear();
962 m_name_id_mapping.clear();
963 m_name_id_mapping_with_aliases.clear();
964 m_group_to_items.clear();
966 m_selection_box_union.reset(0,0,0);
967 m_selection_box_int_union.reset(0,0,0);
969 resetNodeResolveState();
971 u32 initial_length = 0;
972 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
973 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
974 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
975 m_content_features.resize(initial_length);
977 // Set CONTENT_UNKNOWN
981 // Insert directly into containers
982 content_t c = CONTENT_UNKNOWN;
983 m_content_features[c] = f;
984 addNameIdMapping(c, f.name);
991 f.drawtype = NDT_AIRLIKE;
992 f.param_type = CPT_LIGHT;
993 f.light_propagates = true;
994 f.sunlight_propagates = true;
998 f.buildable_to = true;
1000 f.is_ground_content = true;
1001 // Insert directly into containers
1002 content_t c = CONTENT_AIR;
1003 m_content_features[c] = f;
1004 addNameIdMapping(c, f.name);
1007 // Set CONTENT_IGNORE
1011 f.drawtype = NDT_AIRLIKE;
1012 f.param_type = CPT_NONE;
1013 f.light_propagates = false;
1014 f.sunlight_propagates = false;
1016 f.pointable = false;
1018 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1019 f.is_ground_content = true;
1020 // Insert directly into containers
1021 content_t c = CONTENT_IGNORE;
1022 m_content_features[c] = f;
1023 addNameIdMapping(c, f.name);
1028 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1030 std::unordered_map<std::string, content_t>::const_iterator
1031 i = m_name_id_mapping_with_aliases.find(name);
1032 if(i == m_name_id_mapping_with_aliases.end())
1039 content_t NodeDefManager::getId(const std::string &name) const
1041 content_t id = CONTENT_IGNORE;
1047 bool NodeDefManager::getIds(const std::string &name,
1048 std::vector<content_t> &result) const
1050 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1051 if (name.substr(0,6) != "group:") {
1052 content_t id = CONTENT_IGNORE;
1053 bool exists = getId(name, id);
1055 result.push_back(id);
1058 std::string group = name.substr(6);
1060 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1061 i = m_group_to_items.find(group);
1062 if (i == m_group_to_items.end())
1065 const std::vector<content_t> &items = i->second;
1066 result.insert(result.end(), items.begin(), items.end());
1067 //printf("getIds: %dus\n", t.stop());
1072 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1074 content_t id = CONTENT_UNKNOWN;
1080 // returns CONTENT_IGNORE if no free ID found
1081 content_t NodeDefManager::allocateId()
1083 for (content_t id = m_next_id;
1084 id >= m_next_id; // overflow?
1086 while (id >= m_content_features.size()) {
1087 m_content_features.emplace_back();
1089 const ContentFeatures &f = m_content_features[id];
1090 if (f.name.empty()) {
1095 // If we arrive here, an overflow occurred in id.
1096 // That means no ID was found
1097 return CONTENT_IGNORE;
1102 * Returns the smallest box that contains all boxes
1103 * in the vector. Box_union is expanded.
1104 * @param[in] boxes the vector containing the boxes
1105 * @param[in, out] box_union the union of the arguments
1107 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1109 for (const aabb3f &box : boxes) {
1110 box_union->addInternalBox(box);
1116 * Returns a box that contains the nodebox in every case.
1117 * The argument node_union is expanded.
1118 * @param[in] nodebox the nodebox to be measured
1119 * @param[in] features used to decide whether the nodebox
1121 * @param[in, out] box_union the union of the arguments
1123 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1126 switch(nodebox.type) {
1128 case NODEBOX_LEVELED: {
1130 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1131 boxVectorUnion(nodebox.fixed, &half_processed);
1132 // Set leveled boxes to maximal
1133 if (nodebox.type == NODEBOX_LEVELED) {
1134 half_processed.MaxEdge.Y = +BS / 2;
1136 if (features.param_type_2 == CPT2_FACEDIR ||
1137 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1138 // Get maximal coordinate
1140 fabsf(half_processed.MinEdge.X),
1141 fabsf(half_processed.MinEdge.Y),
1142 fabsf(half_processed.MinEdge.Z),
1143 fabsf(half_processed.MaxEdge.X),
1144 fabsf(half_processed.MaxEdge.Y),
1145 fabsf(half_processed.MaxEdge.Z) };
1147 for (float coord : coords) {
1152 // Add the union of all possible rotated boxes
1153 box_union->addInternalPoint(-max, -max, -max);
1154 box_union->addInternalPoint(+max, +max, +max);
1156 box_union->addInternalBox(half_processed);
1160 case NODEBOX_WALLMOUNTED: {
1162 box_union->addInternalBox(nodebox.wall_top);
1163 box_union->addInternalBox(nodebox.wall_bottom);
1164 // Find maximal coordinate in the X-Z plane
1166 fabsf(nodebox.wall_side.MinEdge.X),
1167 fabsf(nodebox.wall_side.MinEdge.Z),
1168 fabsf(nodebox.wall_side.MaxEdge.X),
1169 fabsf(nodebox.wall_side.MaxEdge.Z) };
1171 for (float coord : coords) {
1176 // Add the union of all possible rotated boxes
1177 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1178 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1181 case NODEBOX_CONNECTED: {
1182 // Add all possible connected boxes
1183 boxVectorUnion(nodebox.fixed, box_union);
1184 boxVectorUnion(nodebox.connect_top, box_union);
1185 boxVectorUnion(nodebox.connect_bottom, box_union);
1186 boxVectorUnion(nodebox.connect_front, box_union);
1187 boxVectorUnion(nodebox.connect_left, box_union);
1188 boxVectorUnion(nodebox.connect_back, box_union);
1189 boxVectorUnion(nodebox.connect_right, box_union);
1190 boxVectorUnion(nodebox.disconnected_top, box_union);
1191 boxVectorUnion(nodebox.disconnected_bottom, box_union);
1192 boxVectorUnion(nodebox.disconnected_front, box_union);
1193 boxVectorUnion(nodebox.disconnected_left, box_union);
1194 boxVectorUnion(nodebox.disconnected_back, box_union);
1195 boxVectorUnion(nodebox.disconnected_right, box_union);
1196 boxVectorUnion(nodebox.disconnected, box_union);
1197 boxVectorUnion(nodebox.disconnected_sides, box_union);
1202 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1203 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1209 inline void NodeDefManager::fixSelectionBoxIntUnion()
1211 m_selection_box_int_union.MinEdge.X = floorf(
1212 m_selection_box_union.MinEdge.X / BS + 0.5f);
1213 m_selection_box_int_union.MinEdge.Y = floorf(
1214 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1215 m_selection_box_int_union.MinEdge.Z = floorf(
1216 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1217 m_selection_box_int_union.MaxEdge.X = ceilf(
1218 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1219 m_selection_box_int_union.MaxEdge.Y = ceilf(
1220 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1221 m_selection_box_int_union.MaxEdge.Z = ceilf(
1222 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1226 // IWritableNodeDefManager
1227 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1231 assert(name == def.name);
1233 // Don't allow redefining ignore (but allow air and unknown)
1234 if (name == "ignore") {
1235 warningstream << "NodeDefManager: Ignoring "
1236 "CONTENT_IGNORE redefinition"<<std::endl;
1237 return CONTENT_IGNORE;
1240 content_t id = CONTENT_IGNORE;
1241 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1244 if (id == CONTENT_IGNORE) {
1245 warningstream << "NodeDefManager: Absolute "
1246 "limit reached" << std::endl;
1247 return CONTENT_IGNORE;
1249 assert(id != CONTENT_IGNORE);
1250 addNameIdMapping(id, name);
1252 m_content_features[id] = def;
1253 verbosestream << "NodeDefManager: registering content id \"" << id
1254 << "\": name=\"" << def.name << "\""<<std::endl;
1256 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1257 fixSelectionBoxIntUnion();
1258 // Add this content to the list of all groups it belongs to
1259 // FIXME: This should remove a node from groups it no longer
1260 // belongs to when a node is re-registered
1261 for (const auto &group : def.groups) {
1262 const std::string &group_name = group.first;
1263 m_group_to_items[group_name].push_back(id);
1269 content_t NodeDefManager::allocateDummy(const std::string &name)
1271 assert(name != ""); // Pre-condition
1274 return set(name, f);
1278 void NodeDefManager::removeNode(const std::string &name)
1283 // Erase name from name ID mapping
1284 content_t id = CONTENT_IGNORE;
1285 if (m_name_id_mapping.getId(name, id)) {
1286 m_name_id_mapping.eraseName(name);
1287 m_name_id_mapping_with_aliases.erase(name);
1290 // Erase node content from all groups it belongs to
1291 for (std::unordered_map<std::string, std::vector<content_t>>::iterator iter_groups =
1292 m_group_to_items.begin(); iter_groups != m_group_to_items.end();) {
1293 std::vector<content_t> &items = iter_groups->second;
1294 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1296 // Check if group is empty
1298 m_group_to_items.erase(iter_groups++);
1305 void NodeDefManager::updateAliases(IItemDefManager *idef)
1307 std::set<std::string> all;
1309 m_name_id_mapping_with_aliases.clear();
1310 for (const std::string &name : all) {
1311 const std::string &convert_to = idef->getAlias(name);
1313 if (m_name_id_mapping.getId(convert_to, id)) {
1314 m_name_id_mapping_with_aliases.insert(
1315 std::make_pair(name, id));
1320 void NodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1322 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1323 "overrides to textures from " << override_filepath << std::endl;
1325 std::ifstream infile(override_filepath.c_str());
1328 while (std::getline(infile, line)) {
1330 if (trim(line).empty())
1332 std::vector<std::string> splitted = str_split(line, ' ');
1333 if (splitted.size() != 3) {
1334 errorstream << override_filepath
1335 << ":" << line_c << " Could not apply texture override \""
1336 << line << "\": Syntax error" << std::endl;
1341 if (!getId(splitted[0], id))
1342 continue; // Ignore unknown node
1344 ContentFeatures &nodedef = m_content_features[id];
1346 if (splitted[1] == "top")
1347 nodedef.tiledef[0].name = splitted[2];
1348 else if (splitted[1] == "bottom")
1349 nodedef.tiledef[1].name = splitted[2];
1350 else if (splitted[1] == "right")
1351 nodedef.tiledef[2].name = splitted[2];
1352 else if (splitted[1] == "left")
1353 nodedef.tiledef[3].name = splitted[2];
1354 else if (splitted[1] == "back")
1355 nodedef.tiledef[4].name = splitted[2];
1356 else if (splitted[1] == "front")
1357 nodedef.tiledef[5].name = splitted[2];
1358 else if (splitted[1] == "all" || splitted[1] == "*")
1359 for (TileDef &i : nodedef.tiledef)
1360 i.name = splitted[2];
1361 else if (splitted[1] == "sides")
1362 for (int i = 2; i < 6; i++)
1363 nodedef.tiledef[i].name = splitted[2];
1365 errorstream << override_filepath
1366 << ":" << line_c << " Could not apply texture override \""
1367 << line << "\": Unknown node side \""
1368 << splitted[1] << "\"" << std::endl;
1374 void NodeDefManager::updateTextures(IGameDef *gamedef,
1375 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1376 void *progress_callback_args)
1379 infostream << "NodeDefManager::updateTextures(): Updating "
1380 "textures in node definitions" << std::endl;
1382 Client *client = (Client *)gamedef;
1383 ITextureSource *tsrc = client->tsrc();
1384 IShaderSource *shdsrc = client->getShaderSource();
1385 scene::IMeshManipulator *meshmanip =
1386 RenderingEngine::get_scene_manager()->getMeshManipulator();
1387 TextureSettings tsettings;
1388 tsettings.readSettings();
1390 u32 size = m_content_features.size();
1392 for (u32 i = 0; i < size; i++) {
1393 ContentFeatures *f = &(m_content_features[i]);
1394 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1395 progress_callback(progress_callback_args, i, size);
1400 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1402 writeU8(os, 1); // version
1404 std::ostringstream os2(std::ios::binary);
1405 for (u32 i = 0; i < m_content_features.size(); i++) {
1406 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1407 || i == CONTENT_UNKNOWN)
1409 const ContentFeatures *f = &m_content_features[i];
1410 if (f->name.empty())
1413 // Wrap it in a string to allow different lengths without
1414 // strict version incompatibilities
1415 std::ostringstream wrapper_os(std::ios::binary);
1416 f->serialize(wrapper_os, protocol_version);
1417 os2<<serializeString(wrapper_os.str());
1419 // must not overflow
1420 u16 next = count + 1;
1421 FATAL_ERROR_IF(next < count, "Overflow");
1424 writeU16(os, count);
1425 os << serializeLongString(os2.str());
1429 void NodeDefManager::deSerialize(std::istream &is)
1432 int version = readU8(is);
1434 throw SerializationError("unsupported NodeDefinitionManager version");
1435 u16 count = readU16(is);
1436 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1438 for (u16 n = 0; n < count; n++) {
1439 u16 i = readU16(is2);
1441 // Read it from the string wrapper
1442 std::string wrapper = deSerializeString(is2);
1443 std::istringstream wrapper_is(wrapper, std::ios::binary);
1444 f.deSerialize(wrapper_is);
1446 // Check error conditions
1447 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1448 warningstream << "NodeDefManager::deSerialize(): "
1449 "not changing builtin node " << i << std::endl;
1452 if (f.name.empty()) {
1453 warningstream << "NodeDefManager::deSerialize(): "
1454 "received empty name" << std::endl;
1460 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1461 warningstream << "NodeDefManager::deSerialize(): "
1462 "already defined with different ID: " << f.name << std::endl;
1466 // All is ok, add node definition with the requested ID
1467 if (i >= m_content_features.size())
1468 m_content_features.resize((u32)(i) + 1);
1469 m_content_features[i] = f;
1470 addNameIdMapping(i, f.name);
1471 verbosestream << "deserialized " << f.name << std::endl;
1473 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1474 fixSelectionBoxIntUnion();
1479 void NodeDefManager::addNameIdMapping(content_t i, std::string name)
1481 m_name_id_mapping.set(i, name);
1482 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1486 NodeDefManager *createNodeDefManager()
1488 return new NodeDefManager();
1492 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1495 if (m_node_registration_complete)
1496 nr->nodeResolveInternal();
1498 m_pending_resolve_callbacks.push_back(nr);
1502 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1504 size_t len = m_pending_resolve_callbacks.size();
1505 for (size_t i = 0; i != len; i++) {
1506 if (nr != m_pending_resolve_callbacks[i])
1510 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1511 m_pending_resolve_callbacks.resize(len);
1519 void NodeDefManager::runNodeResolveCallbacks()
1521 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1522 NodeResolver *nr = m_pending_resolve_callbacks[i];
1523 nr->nodeResolveInternal();
1526 m_pending_resolve_callbacks.clear();
1530 void NodeDefManager::resetNodeResolveState()
1532 m_node_registration_complete = false;
1533 m_pending_resolve_callbacks.clear();
1536 void NodeDefManager::mapNodeboxConnections()
1538 for (ContentFeatures &f : m_content_features) {
1539 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1542 for (const std::string &name : f.connects_to) {
1543 getIds(name, f.connects_to_ids);
1548 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1549 u8 connect_face) const
1551 const ContentFeatures &f1 = get(from);
1553 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1556 // lookup target in connected set
1557 if (!CONTAINS(f1.connects_to_ids, to.param0))
1560 const ContentFeatures &f2 = get(to);
1562 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1563 // ignores actually looking if back connection exists
1564 return CONTAINS(f2.connects_to_ids, from.param0);
1566 // does to node declare usable faces?
1567 if (f2.connect_sides > 0) {
1568 if ((f2.param_type_2 == CPT2_FACEDIR ||
1569 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1570 && (connect_face >= 4)) {
1571 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1572 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1574 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1576 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1577 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1579 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1580 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1581 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1583 return (f2.connect_sides
1584 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1586 return (f2.connect_sides & connect_face);
1588 // the target is just a regular node, so connect no matter back connection
1596 NodeResolver::NodeResolver()
1598 m_nodenames.reserve(16);
1599 m_nnlistsizes.reserve(4);
1603 NodeResolver::~NodeResolver()
1605 if (!m_resolve_done && m_ndef)
1606 m_ndef->cancelNodeResolveCallback(this);
1610 void NodeResolver::nodeResolveInternal()
1612 m_nodenames_idx = 0;
1613 m_nnlistsizes_idx = 0;
1616 m_resolve_done = true;
1618 m_nodenames.clear();
1619 m_nnlistsizes.clear();
1623 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1624 const std::string &node_alt, content_t c_fallback)
1626 if (m_nodenames_idx == m_nodenames.size()) {
1627 *result_out = c_fallback;
1628 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1633 std::string name = m_nodenames[m_nodenames_idx++];
1635 bool success = m_ndef->getId(name, c);
1636 if (!success && !node_alt.empty()) {
1638 success = m_ndef->getId(name, c);
1642 errorstream << "NodeResolver: failed to resolve node name '" << name
1643 << "'." << std::endl;
1652 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1653 bool all_required, content_t c_fallback)
1655 bool success = true;
1657 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1658 errorstream << "NodeResolver: no more node lists" << std::endl;
1662 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1665 if (m_nodenames_idx == m_nodenames.size()) {
1666 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1671 std::string &name = m_nodenames[m_nodenames_idx++];
1673 if (name.substr(0,6) != "group:") {
1674 if (m_ndef->getId(name, c)) {
1675 result_out->push_back(c);
1676 } else if (all_required) {
1677 errorstream << "NodeResolver: failed to resolve node name '"
1678 << name << "'." << std::endl;
1679 result_out->push_back(c_fallback);
1683 m_ndef->getIds(name, *result_out);